DRFのModelSerializerを使用する際にfield名に存在しない値を扱う場合の処理

Django

sourceを使用する

sourceを使用することで、Metaに指定したモデルに関連する値を抜き出すことが出来ます

使い方としては以下のような感じです。

models.py

class Pref(models.Model):
    name = models.CharField(...)
    point = models.PointField(geography=True...)

    def get_lat(self):
        return self.point.y

    def get_long(self):
        return self.point.x
    ...

class City(models.Model):
    city = models.ForeignKey(Pref, related_name='cities')
    name = models.CharField(...)
    ...

serializers.py

class PrefSerializer(serializers.ModelSerializer):
    city_count =  serializers.IntegerField(source='cities.count', read_only=True)

    class Meta:
        model = Pref
        fields = ('id', 'name', 'city_count')

このようにrelated_nameを付けた名称からcountを取ったり、上記のモデルからであればget_latやget_longを指定することも出来ます。

SerializerMethodFieldを使用する

SerializerMethodFieldを使用すると、serializerの中でfield名に沿った独自の関数を作成することが出来ます

使い方としては以下のような感じです。get_ + field名で関数を作るところが特徴的です。

serializers.py

class PrefSerializer(serializers.ModelSerializer):
    is_pickup =  serializers. SerializerMethodField(read_only=True)

    class Meta:
        model = Pref
        fields = ('id', 'name', 'is_pickup')

    def get_is_pickup(self, instance):
        return instance.pickup_start <= timezone.now() < instance.pickup_end

上記のもの程度であれば、モデル内に関数を用意してsourceを使ってしまっても良いですが、ちょっとした判定などを行うのに使うと便利です。

ただし、自由度が非常に高いあまりに、この中で複雑な処理を書くことはパフォーマンスの観点から避けた方が良いと思います。

to_representationを使用する

to_representation関数をoverrideすることで、view側のquerysetでannotateなどで生成した変数名を使いたい時や、データの階層を変えたい時などに使うことが出来ます

views.py

class PrefList(mixins.ListModelMixin, generics.GenericAPIView):
    permission_classes = (AllowAny,)
    serializer_class = PrefSerializer
    queryset = Pref.objects.annotate(
                rank=Window(
                    expression=Rank(),
                    order_by=F('population').desc()
                )
            )

serializers.py

class PrefSerializer(serializers.ModelSerializer):
    class Meta:
        model = Pref
        fields = ('id', 'name')

    def to_representation(self, instance):
        ret = super(PrefSerializer, self).to_representation(instance)
        ret['rank'] = instance.rank
        return ret

上記の例では、views側で生成したrankをto_representationで入れています。fieldsに入れようとすると、そのようなfieldは無いとエラーが出てしまいます。

最後に

モデル内に存在しないfieldを返す方法をいくつか上げてみました。

ただし、どの方法も注意しないといけないのは、querysetでrelationなどを良く考えながらselect_relatedやprefetch_relatedなどを加えてパフォーマンスを保つことには気を付けましょう。

コメントを残す