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などを加えてパフォーマンスを保つことには気を付けましょう。