[DRF]ModelSerializerのvalidation


django-rest-frameworkを学び始めました。関連の記事を書いていきます。

ちょっと裏で動いていることが多くてなかなか理解しづらいですが、簡単なところから記事にいていこうと思います。

djangoのプロジェクト作成とか、django-rest-frameworkとはってところとか、シリアライザってなに?ってところは端折ります(別で記事にするかもしれません)。

ではいきます。

ModelSerializerのvalidationについて

ModelSerializerを継承したselializerの中で以下のように書くと、項目名に対して独自のバリデーションを定義することができます。

serializers.py
class XXXXSerializer(serializers.ModelSerializer):

    class Meta:
        model = XXXX
        fields = "__all__"

    def validate_項目名(self, value):
        if 条件:
            raise serializers.ValidationError(
                エラー時の挙動
            )
        return value

これに対して、viewから以下のようにして呼びます。

views.py
serializer = XXXXSerializer(data=data)
serializer.is_valid()

is_valid()で なにが呼ばれているかという話ですね。
順番に追っていきましょう。

まず、BaseSerializeris_valid()メソッドに行き着きます。
その中のself.run_validation(self.initial_data)が呼ばれます。ここで、serializerはModelSerializerを継承しているので、BaseSerializerのサブクラスのSerializerやModelSerializerでrun_validationがあればそちらが呼ばれますね。

今回はSerializerにだけあったので、Serializerのrun_validationが呼ばれます。
その中のself.to_internal_value(data)が呼ばれ、見たかったものがありました。

to_internal_value
    def to_internal_value(self, data):
        """
        Dict of native values <- Dict of primitive datatypes.
        """
        if not isinstance(data, Mapping):
            message = self.error_messages['invalid'].format(
                datatype=type(data).__name__
            )
            raise ValidationError({
                api_settings.NON_FIELD_ERRORS_KEY: [message]
            }, code='invalid')

        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields

        for field in fields:
            # ここ!!!
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
            try:
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret

#ここ!!ってかいてあるところですね。
多分ですが項目ごとの標準のvalidateが呼ばれたあとに独自のvalidateが呼ばれる感じなんですかね。

validate_methodという変数に、フィールドごとのvalidateメソッドを集めて、
validated_value = validate_method(validated_value)で実行してますね。
pythonだとメソッド書くときにこんな書き方が出来るんですかね?

getattr関数で出来るみたいですね。便利。。
これは使い方覚えておきたいとことですね。
以下のように書くとメソッドを文字列から呼べるみたいです。

getattrの例
class test:
    def a(self):
        return 5

    def b(self,i):
        print("b" + str(i))

    def c(self,i,j):
        print("b"+str(i)+str(j))


testclass = test()


print(getattr(testclass,"a")())

d = getattr(testclass, "b")(6)

e = getattr(testclass, "c")(7,8)

基本的なことだと思いますが、とりあえず一つ知れて良かったです。
getattrなどについてはまた詳しくやりたいですね。

では今回はここまでです!