DRFのシリアライザーでnull値をblankにしたかった

今開発中のアプリでクライアントから受け取ったデータからnullを取り除いて、blankに変更したかった。

流れとしては

  1. クライアントのフォームから値を送信
  2. サーバーで値を受け取る

で、1のクライアントからデータを受け取った際、値に何も入っていないデータはnullで届くようになっているが、serializerのバリデーションで弾かれる。

フォームからの送信時にnullをブランクにしても良かったのだが、とりあえずdjango側でnullをblankにcastすることにした。 値を受け取ってからDRFがvalidationを実行しsaveするまでの大まかな流れは以下である

  1. view
  2. to_internal_value (serializer)
  3. save (serializer)

上記の流れから、nullをblankにキャストする際は1のViewまたは2のserializer内で行うことができる。 値のキャストを担う機能をどこで行うか考えたところ、serializerで行うことにした。

複雑なデータのやりくりであればserializerでやるべきではないのだが、今回のようなシンプルな値の変換はserializer内のto_internal_valueで行うのが最適という結論に至った。 理由としては、2のメソッドはserializerのバリデーション実行前、書き込み時のみに実行されるからだ。 また、serializerの持つfieldを扱うことができるので楽だなと。 ということで、以下のようにコードを実装した。

class User(ModelSerializer):
  class Meta:
    model = User
    fields = "__all__"

  def to_internal_value(self, data):
    cast_blank_fields = ["non_blank_field"]
    fields = self._writable_fields
    for field in fields:
      field_name = field.field_name
      if field_name in cast_blank_fields:
        if not field.get_value(data):
          data[field_name] = ""

    return super().to_internal_value(data)

上記のように、to_internal_valueをオバーライドしつつ、そのベースクラスの同メソッドを呼ぶことで、上書きしたあとにもベースクラスのバリデーションも実行できるという手段である。