使用 to_representation 组合 Django Rest Framework 中相关模型的字段

Use to_representation to combine fields from related models in Django Rest Framework

我有以下型号:-

class Player(models.Model):
    first_name = models.CharField()
    etc.

class Game(models.Model):
    date = models.DateField()
    etc.

class GameMembership(models.Model):
    player = models.ForeignKey(Player, related_name="memberships")
    game = models.ForeignKey(Game, related_name="memberships")
    available = models.BooleanField(default=False)

我已经为 return 所有玩家创建了一个 ModelViewSet,但我希望能够为列表中的每个玩家 return 列出他们的游戏会员资格。我可以很容易地做到这一点,但是 returns 的数据看起来像这样:-

{
    "id": "1",
    "memberships": [
        {
            "available": True,
            "game": {
                "date": "a date",
                etc.
            }
        },
        {
            "available": False,
            "game": {
                "date": "a date",
                etc.
            }
        }
    ]
}

但我想对 API 用户隐藏我的数据库的“成员资格”方面,而 return 则像这样:-

{
    "id": "1",
    "games": [
        {
            "available": True,
            "date": "a date",
            etc.
        },
        {
            "available": False,
            "date": "a date",
            etc.
        }
    ]
},

所以我想从 GameMembership 模型中提取一个(或两个)字段并将其与 Game 模型中的所有字段组合,但至关重要的是,我希望将其全部放入 returned 中的一个字典中结果。我知道我可以简单地在 GameMembershipSerializer 上序列化 Game,但这意味着我将 returning:-

{
    "id": "1",
    "games": [
        {
            "available": True,
            "game": {
                "date": "a date",
                etc.
            }
        },
        {
            "available": False,
            "game": {
                "date": "a date",
                etc.
            }
        }
    ]
}

这实际上没有意义,因为用户将不得不访问诸如结果['games'][1]['game']之类的东西,这似乎是错误的。

我以为我可以通过在 GameMembershipSerializer 上使用 to_representation 来做到这一点,但我想不出来。

有什么想法吗?

另一种不覆盖 to_representation 的方法是使用 SerializerMethodField 并像这样进行处理:

class GameModelSerializer(models.ModelSerializer):
    class Meta:
        model = Game
        fields = '__all__'


class PlayerModelSerializer(models.ModelSerializer):
    games = serializers.SerializerMethodField()

    class Meta:
        model = Player
        fields = ('id', 'games')

    def get_games(self, player)
        return [{
            'available': membership.available,
            **GameModelSerializer(membership.game).data,
        } for membership in player.memberships.all()]

并且为了确保序列化程序不会仅仅为了获得游戏而按会员资格访问数据库,您可以像这样选择游戏来预取会员资格:

Player.objects.prefetch_related(
    Prefetch('memberships', queryset=GameMembership.objects.select_related('game'))
)

编辑: 你也可以这样做:

    def get_games(self, player)
        return [{
            **GameMembershipSerializer(membership).data,
            **GameModelSerializer(membership.game).data,
        } for membership in player.memberships.all()]

如果您想重复使用 GameMembershipSerializer

对于那些对我最终如何解决这个问题感兴趣的人,我需要使用 to_representation 因为它需要是一个可扩展的字段(使用 drf-flex-fields)。我还发现以下实现比上面提到的代码快得多,所以我改用它:-

# PlayerSerializer

games = GameMembershipGameSerializer(many=True)


class GameMembershipGameSerializer(serializers.ModelSerializer):
    class Meta:
        model = GameMembership
        fields = ["available", "game"]

    def to_representation(self, membership):
        representation = super().to_representation(membership)
        game = representation.pop("game")

        for key in game:
            representation[key] = game[key]

        return representation