django-rest-frameworkのserializerでいろんなメソッドの呼ばれる順番


この記事の目的

  • django-rest-framework を使っていて、serializerが中でどういう処理をしているのかがわからなくてつらい
  • そうだ、フレームワークの中身を読んでみよう

環境

  • djangorestframework: 3.9.1

見るところ

  • rest_framework/serializers.py
    • class Serializer(BaseSerializer)の辺り
    • とりあえずここ理解すれば他もなんとかなる?

読む

  • renderersとかmixinsとかは後回しでいきなりserializers

to_internal_value(self, data)

 471     def to_internal_value(self, data):
 472         """
 473         Dict of native values <- Dict of primitive datatypes.
 474         """
 475         if not isinstance(data, Mapping):
 476             message = self.error_messages['invalid'].format(
 477                 datatype=type(data).__name__
 478             )
 479             raise ValidationError({
 480                 api_settings.NON_FIELD_ERRORS_KEY: [message]
 481             }, code='invalid')
  • 受け取ったデータ型の検証とか?

 483         ret = OrderedDict()
 484         errors = OrderedDict()
 485         fields = self._writable_fields
  • ret: 最終的な返却値
  • errors: 例外情報
  • fields: to_internal_valueで処理する対象が格納されている。
    • _writable_fieldsは以下の通り。
 367     @cached_property
 368     def _writable_fields(self):
 369         return [
 370             field for field in self.fields.values() if not field.read_only
 371         ]
  • read_onlyプロパティがFalseになっているfieldだけが含まれるらしい。
 487         for field in fields:
 488             validate_method = getattr(self, 'validate_' + field.field_name, None)
 489             primitive_value = field.get_value(data)
 490             try:
 491                 validated_value = field.run_validation(primitive_value)
  • fieldのrun_vaildationを実行
 523     def run_validation(self, data=empty):
 524         """
 525         Validate a simple representation and return the internal value.
 526
 527         The provided data may be `empty` if no representation was included
 528         in the input.
 529
 530         May raise `SkipField` if the field should not be included in the
 531         validated data.
 532         """
 533         (is_empty_value, data) = self.validate_empty_values(data)
 534         if is_empty_value:
 535             return data
 536         value = self.to_internal_value(data)
 537         self.run_validators(value)
 538         return value
  • 渡ってきた値のto_internal_valueを呼び出し。
    • ここの処理はまた今度読む
 492                 if validate_method is not None:
 493                     validated_value = validate_method(validated_value)
  • validate_<field名> という名前でメソッドが宣言されてたらそれを実行する
 494             except ValidationError as exc:
 495                 errors[field.field_name] = exc.detail
 496             except DjangoValidationError as exc:
 497                 errors[field.field_name] = get_error_detail(exc)
 498             except SkipField:
 499                 pass
  • 例外が発生したらerrorsに入れる
 500             else:
 501                 set_value(ret, field.source_attrs, validated_value)
  • validate_のメソッドを実行した結果をretに入れる
 503         if errors:
 504             raise ValidationError(errors)
  • 何かしらのエラーがあったら例外発火
 506         return ret

to_representation

 508     def to_representation(self, instance):
 509         """
 510         Object instance -> Dict of primitive datatypes.
 511         """
 512         ret = OrderedDict()
 513         fields = self._readable_fields
  • fields: _readable_fieldsで取得
    • _readable_fieldsは以下の処理
    • write_onlyでないフィールドを取得している
 373     @cached_property
 374     def _readable_fields(self):
 375         return [
 376             field for field in self.fields.values()
 377             if not field.write_only
 378         ]
 514
 515         for field in fields:
 516             try:
 517                 attribute = field.get_attribute(instance)
 518             except SkipField:
 519                 continue
  • instanceから指定のフィールドに格納されているデータを取得
 521             # We skip `to_representation` for `None` values so that fields do
 522             # not have to explicitly deal with that case.
 523             #
 524             # For related fields with `use_pk_only_optimization` we need to
 525             # resolve the pk value.
 526             check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
 527             if check_for_none is None:
 528                 ret[field.field_name] = None
 529             else:
 530                 ret[field.field_name] = field.to_representation(attribute)
  • コメントのとおり…
  • to_internal_valueと同様に、fieldのto_representationも順に呼び出される
 531
 532         return ret

まとめ

  • to_internal_value
    • 各fieldに対してto_internal_valueとvalidationを繰り返してる
    • オブジェクトがネストされているときは、深いところからto_internal_valuevalidate_<field名> が実行されることになりそう
  • to_representation
    • fieldto_representationを呼び出してるくらい?
  • field.to_internal_valueとかfield.to_representationはまた今度読む