【问题标题】:Use to_representation to combine fields from related models in Django Rest Framework在 Django Rest Framework 中使用 to_representation 组合来自相关模型的字段
【发布时间】:2021-12-24 02:34:33
【问题描述】:

我有以下型号:-

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)

我创建了一个 ModelViewSet 来返回所有玩家,但我希望能够为列表中的每个玩家返回他们的游戏成员列表。我可以很容易地做到这一点,但它返回的数据如下所示:-

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

但我想对 API 用户隐藏我的数据库的“成员资格”方面并返回类似的内容:-

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

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

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

这没有任何意义,因为用户必须访问 results['games'][1]['game'] 之类的东西,这似乎是错误的。

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

有什么想法吗?

【问题讨论】:

    标签: django django-models django-rest-framework django-queryset


    【解决方案1】:

    对于那些对我最终如何解决这个问题感兴趣的人,我需要使用 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
    

    【讨论】:

      【解决方案2】:

      另一种不覆盖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

      【讨论】:

      • 这样做的问题是我将不得不手动从 GameMembership 添加我想要的所有字段,而不是使用该模型已经存在的 GameMembershipSerializer,这将是容易得多。
      • 啊在这种情况下你可以把'available': membership.available,改成** GameMembershipSerializer(membership).data,
      • 非常好 - 感谢您的帮助。最后,我需要使用 to_representation,因为这是一个可选的可扩展字段(使用 drf-flex-fields),但这个答案让我开始着手解决这一切!
      猜你喜欢
      • 1970-01-01
      • 2016-03-15
      • 1970-01-01
      • 2013-01-12
      • 2021-12-31
      • 1970-01-01
      • 2021-11-18
      • 2015-10-27
      • 2018-11-11
      相关资源
      最近更新 更多