ホーム >  Python > Django REST framework >  【API】DRFのバリデーションのやり方【Django】

投稿日:   |  最終更新日:

【API】DRFのバリデーションのやり方【Django】

Django REST frameworkPython

DRFのシリアライザを利用し、バリデーションを行います。

DRFでバリデーションをするには?

シリアライザを使うことで、Django入力フォームと同じように入力情報のバリデーションをおこなうことができます。

  • ①検証対象の入力データを引数dataに渡します。
  • ②シリアライザをインスタンス化します。
  • ③「is_valid()」を実行することで入力データのバリデーションをおこなうことができます。

バリデーションの対象フィールド

バリデーションの対象となるのは次のフィールドです。

登録・更新 すべての入力用(つまり read_only=True ではない)フィールド
一部更新 引数dataに含まれるすべての入力用(read_only=True ではない) フィールド

モデルのフィールドオプションで 「editable=False」が指定されているフィールドは「read_only=True」 になるため、 バリデーションの対象外です。

バリデーションのOKとNG

バリデーションOK

is_valid() の戻り値がTrue(すなわちバリデーション OK)の場合、 シリアライザオブジェクトの 「validated_data」 という属性にアクセスすることで、 バリデーション済みの値が保持された「OrderedDict型」のオブジェクトを取得することができます。

from api.serializers import BookSerializer  

serializer = BookSerializer(data={'title': 'DRFの本', 'price': 1500})
serializer.is_valid()
>>True  

serializer.validated_data
OrderedDict([('title', 'DRFの本'), ('price', 1500)])

serializer.errors  
>>{} 

バリデーションNG

バリデーションNGの場合は、「errors」という属性です。

フィールド名とエラーの詳細を保持した「OrderedDict型」のオブジェクトを取得することができます。 この場合、「validated_data」 は空の dictになるので注意が必要です。IntegerFieldの「price」に「aaa」という文字列を入れると、is_valid() の戻り値はFalse になります。

serializer = BookSerializer(data={'title': 'DRFの本', 'price': 'aaa'})  
serializer.is_valid()  
>>False

serializer.errors
>>{'price': [ErrorDetail(string='有効な整数を入力してください。', code='invalid')]} 

serializer.validated_data
>>{} 

また次のように raise_exception=True を指定して is_valid() を実行すると、 バリデーションNGの場合「rest_framework.exceptions.ValidationError」がraiseされます。後述する汎用APIViewやModelViewSetでは、この仕組みを利用してバリデーションNG時のエラーメッセージを伝播しています。

serializer = BookSerializer(data={'title': 'DRFの本', 'price': 'aaa'})  
serializer.is_valid(raise_exception=True)  
>>Traceback (most recent call last):  
>>... (略)...  
>>raise ValidationError(self.errors)
>>rest_framework.exceptions.ValidationError: {'price': [ErrorDetail(string='有効な  整数を入力してください。', code='invalid')]}

ところで、 is_valid() が実行されると、 シリアライザとそのフィールドに定義されたバ リデーションメソッドが、 決まった順序で次々 と実行されます。

シリアライザのバリデーションフロー

①シリアライザの「run_validation()」が呼ばれます。

②その中で、 シリアライザに定義した入力用フィールドの「run_validation()」が呼び出されます。

フィールドの「run_validation()」では、フィールドの種類に応じた文字種チェックなどのデフォルトのバリデーションのほか、 フィールドのvalidators属性に定義したバリデーションが実行されます。

③フィールドがシリアライザの場合、再帰的に run_validation() が実行されていきます。

④シリアライザに 「validate_<フィールド名>」 というメソッドが定義されている場合、それが実行されます。

ここまでが、 バリデーション対象のフィールドの数だけ繰り返されます。

バリデーションが全てOK

シリアライザ自身の「run_validators()」が呼ばれてMetaクラスの「validators」に定義されたバリデーションが実行されます。 ここまでのバリデーションがすべてOKの場合、最後にvalidate()メソッドが呼ばれます。

これらはいずれも、複数フィールド間のバリデーションを行うためです。


バリデーションの追加方法

ModelSerializer や Serializerを継承したシリアライザにバリデーションを追加する方法としては、以下の4つほどが挙げられます。

  • 1.Meta クラスの extra_kwargs に validators を追加
  • 2.validate_<フィールド名>() メソッドを追加
  • 3.Meta クラスの validators に追加
  • 4.validate() メソッドを追加

1.と 2.が実行された後に3.が実行され、 最後に4.が実行されます。1.および2.は単体のフィールドに対するバリデーションです。

これらのバリデーションは、「read_only=True」のフィールド、 および一部更新の場合で入力データに含まれていないフィールドに対しては実行されません。

3. および 4.では複数フィールド間の相関チェックなど単体のフィールドに紐付かないバリデーションを行います。

3.は 1.と2.のバリデーションが OK になっていないと実行されません。

4.も同様に 1. から 3. までのバリデーションが OK でないと実行されません。なお、Serializer を継承したシリアライザでは、1.の方法は利用できません。

以下の例は、 シリアライザクラスに1.~4.までのバリデーションを加えたサンプルコードです。

from django.core.validators import RegexValidator
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from .models import Book

class BookSerializer(serializers.ModelSerializer):
  class Meta:
    model = Book
    exclude = ['created_at']
        validators = [
          #タイトルと価格でユニークになっていることを検証
          UniqueTogetherValidator(queryset=Book.objects.all(),
          fields=('title', 'price'),
            message="タイトルと価格でユニークになっていなければいけません。  " 
          ),
        ]
        extra_kwargs = {
          'title': {
            'validators': [
               RegexValidator(  r'^D.+$', message="タイトルは 「D」 で始めてください。"),
            ],
          },  
        }
  
  def validate_title(self, value):
    """タイトルに対するバリデーションメソッド"""  
    if 'Java' in value:
        raise serializers.ValidationError(
            "タイトルには 「Java」 を含めないでください。")
     return value

  def validate(self, data):
    """複数フィールド間のバリデーションメソッド"""
    title = data.get('title')
    price = data.get('price')
    if title and '薄い本' in title and price and price > 3000:
      raise serializers.ValidationError(  "薄い本は3,000円を超えてはいけません。")
    return data  1. や 3. の validators

ここで、シリアライザを使ったViewに以下の入力データをPOSTで送信します。

{
  "title":"Javaの薄い本",
  "price":"aaa"
}

以下のようなJsonレスポンスが返信されます。

{
  "title":["タイトルは「D」で始めてください。"],
  "price":["有効な整数を入力してください。"]
}

次回

工事中。


トラックバック用のURL
プロフィール

名前:イワサキ ユウタ 職業:システムエンジニア、ウェブマスター、フロントエンドエンジニア 誕生:1986年生まれ 出身:静岡県 特技:ウッドベース 略歴 20

最近の投稿
人気記事
カテゴリー
広告