自分用忘備録
逐次追加します
基本的にto_representation()
とto_internal_value()
オーバーライドすれば何とかなる。
環境
- Python 3.8
- django 3.0.6
- djangorestframework 3.11.0
特定のフィールドでグループ化(group by)してネスト
やりたいこと
次のようなmodel
があったとき
class Message(models.Model):
user = models.ForeignKey('User', related_name="messages", on_delete=models.CASCADE)
messsage = models.TextField()
デフォルトのSerializer
でリスト化すると次のようになるところを
[
{"id": 1, "user": 1, "message": "あああ"},
{"id": 2, "user": 1, "message": "いいい"},
{"id": 3, "user": 2, "message": "ううう"},
{"id": 4, "user": 2, "message": "えええ"},
{"id": 5, "user": 3, "message": "おおお"},
]
次のように取得したい。
{
"1": [{"id": 1, "user": 1, "message": "あああ"}, {"id": 2, "user": 1, "message": "いいい"}],
"2": [{"id": 3, "user": 2, "message": "ううう"}, {"id": 4, "user": 2, "message": "えええ"}],
"3": [{"id": 5, "user": 3, "message": "おおお"}]
}
やりかた
# Serializerの定義
from rest_framework import serializers
from rest_framework.utils.serializer_helpers import ReturnDict
class _MessageGroupByUserSerializer(serializers.ListSerializer):
def to_representation(self, data):
_to_representation = super().to_representation # 【Point 1】
return [{ # 【Point 2】
user: _to_representation(Message.objects.filter(user=user))
for user in Message.objects.values_list('user', flat=True).order_by('user').distinct()
}]
@property
def data(self):
return ReturnDict(super().data[0], serializer=self) # 【Point 3】
class MessageGroupByUserSerializer(serializers.ModelSerializer):
class Meta:
list_serializer_class = _MessageGroupByUserSerializer
model = Message
fields = '__all__'
- 【Point 1】
return
でdict
を初期化する際に直接super().to_representation
とやりたいところだが、dict
自身のsuper()
を参照してしまうので関数ポインタを退避 - 【Point 2】
ListSerializer
のデフォルト処理で変換できるようにlist
で渡してやる - 【Point 3】Point 2で編集した形式の
list
になってるのでdict
を取り出してやる。なお親クラスのListSerializer
ではReturnList
で変化している。
あとは普通にview
で呼ぶだけ。
class MessageGroupByUser(generics.ListAPIView):
queryset = Message.objects
serializer_class = MessageGroupByUserSerializer
permission_classes = (permissions.IsAuthenticated,)
ネストされたフィールドを平坦化(flatten)する
やりたいこと
次のようなmodel
とSerializer
があったとき
class HogeHoge(models.Model):
name = models.CharField(max_length=150)
deleted = models.BooleanField(default=False)
class FugaFuga(models.Model):
name = models.CharField(max_length=150)
deleted = models.BooleanField(default=False)
class Message(models.Model):
hogehoge= models.ForeignKey('HogeHoge', on_delete=models.CASCADE)
fugafuga= models.ForeignKey('FugaFuga', on_delete=models.CASCADE)
messsage = models.TextField()
class HogeHogeSerializer(serializers.ModelSerializer):
class Meta:
model = HogeHoge
fields = '__all__'
read_only_fields = ('id', )
class FugaFugaSerializer(serializers.ModelSerializer):
class Meta:
model = FugaFuga
fields = '__all__'
read_only_fields = ('id', )
class MessageSerializer(serializers.ModelSerializer):
hogehoge = HogeHogeSerializer()
fugafuga = FugaFugaSerializer()
class Meta:
model = Message
fields = ('id', 'hogehoge', 'fugafuga', 'message')
read_only_fields = ('id', )
MessageSerializer
が次のように取得されるところを、
{
"id": 1,
"hogehoge": {"id": 2, "name": "ほげほげ2", "deleted": false},
"fugafuga": {"id": 3, "name": "ふがふが3", "deleted": true},
"message": "あああ"
}
次のように取得したい。
{
"id": 1,
"hogehoge_id": 2,
"hogehoge_name": "ほげほげ2",
"hogehoge_deleted": false,
"fugafuga_id": 3,
"fugafuga_name": "ふがふが3",
"fugafuga_deleted": true,
"message": "あああ"
}
やりかた
class MessageSerializer(serializers.ModelSerializer):
hogehoge = HogeHogeSerializer()
fugafuga = FugaFugaSerializer()
class Meta:
model = Message
fields = ('id', 'hogehoge', 'fugafuga', 'message')
read_only_fields = ('id', )
def to_representation(self, obj):
representation = super().to_representation(obj) # 【Point 1】
for field in ('hogehoge', 'fugafuga'):
inner_representation = representation.pop(field) # 【Point 2】
if inner_representation:
for key in inner_representation: # 【Point 3】
representation[field + "_" + key] = inner_representation[key]
return representation
- 【Point 1】まず元の処理を行ってしまう
- 【Point 2】処理済みデータからネストされたフィールドを取り除く
- 【Point 3】新たなフィールド名を付けて追加する