Django REST Frameworkで複雑な形式のSerializerを実装する

自分用忘備録
逐次追加します

基本的に`to_representation()`と`to_internal_value()`オーバーライドすれば何とかなる。

## 環境
* Python 3.8
* django 3.0.6
* djangorestframework 3.11.0

## 特定のフィールドでグループ化(group by)してネスト
[^groupby]
[^groupby]: (~~あまり参考にならない~~)参考:https://stackoverflow.com/questions/31071702/django-restframework-group-by


### やりたいこと
次のような`model`があったとき

```python
class Message(models.Model):
    user = models.ForeignKey('User', related_name="messages", on_delete=models.CASCADE)
    messsage = models.TextField()
```
デフォルトの`Serializer`でリスト化すると次のようになるところを

```json
[
  {"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": "おおお"},
]
```

次のように取得したい。

```json
{
  "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": "おおお"}]
}
```

### やりかた
```python
# 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`で呼ぶだけ。

```python
class MessageGroupByUser(generics.ListAPIView):
    queryset = Message.objects
    serializer_class = MessageGroupByUserSerializer
    permission_classes = (permissions.IsAuthenticated,)
```

## ネストされたフィールドを平坦化(flatten)する
[^flatten]
[^flatten]: 参考:https://stackoverflow.com/questions/21381700/django-rest-framework-how-do-you-flatten-nested-data

### やりたいこと
次のような`model`と`Serializer`があったとき

```python
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`が次のように取得されるところを、

```json
{
  "id": 1,
  "hogehoge": {"id": 2, "name": "ほげほげ2", "deleted": false},
  "fugafuga": {"id": 3, "name": "ふがふが3", "deleted": true},
  "message": "あああ"
}
```

次のように取得したい。

```json
{
  "id": 1,
  "hogehoge_id": 2,
  "hogehoge_name": "ほげほげ2",
  "hogehoge_deleted": false,
  "fugafuga_id": 3,
  "fugafuga_name": "ふがふが3",
  "fugafuga_deleted": true,
  "message": "あああ"
}
```

### やりかた
```python
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】新たなフィールド名を付けて追加する