【问题标题】:How to display properties of a nested object in Django Rest Framework如何在 Django Rest Framework 中显示嵌套对象的属性
【发布时间】:2019-12-12 12:37:54
【问题描述】:

我正在使用 Django 和 Django-Rest-Framework 为战斗系统构建 API。在我的代码中,我有 2 个模型:父模型 Battle 和子模型 RoundRound 有一些 @property 字段 (start_time, end_time, score),它们是根据不同的值计算的。当我直接访问Round 路由时,我得到了所需的输出:

http://127.0.0.1:8001/battle/rounds/1/

{
    "id": 1,
    "battle": "http://127.0.0.1:8001/battle/battles/1/",
    "index": 0,
    "contender_entry": null,
    "opponent_entry": null,
    "start_time": "2019-12-11T17:38:00Z",
    "end_time": "2019-12-11T17:39:40Z",
    "score": [
        0,
        0
    ]
}

但是,当我访问Battle 路由时,会返回嵌套的Rounds,但返回数据库字段,而不返回属性:

http://127.0.0.1:8001/battle/battles/1/

{
    "url": "http://127.0.0.1:8001/battle/battles/1/",
    "id": 1,
    "status": "live",
    "start_time": "2019-12-11T17:38:00Z",
    "round_length": "00:01:40",
    ...
    "rounds": [
        {
            "url": "http://127.0.0.1:8001/battle/rounds/1/",
            "beat": null,
            "index": 0,
            "battle": "http://127.0.0.1:8001/battle/battles/1/",
            "contender_entry": null,
            "opponent_entry": null
        },
        {
            "url": "http://127.0.0.1:8001/battle/rounds/2/",
            "beat": null,
            "index": 1,
            "battle": "http://127.0.0.1:8001/battle/battles/1/",
            "contender_entry": null,
            "opponent_entry": null
        },
        {
            "url": "http://127.0.0.1:8001/battle/rounds/3/",
            "beat": null,
            "index": 2,
            "battle": "http://127.0.0.1:8001/battle/battles/1/",
            "contender_entry": null,
            "opponent_entry": null
        }
    ],
    "current_round": null
}

我希望属性显示在Battle 中的嵌套Round 对象中。但我无法让它工作。

这些是我的模型:

class Round(models.Model):
    battle = models.ForeignKey(Battle, on_delete=models.CASCADE, related_name="rounds")
    index = models.IntegerField()
    contender_entry = models.OneToOneField(Entry, on_delete=models.DO_NOTHING, related_name="round_contender",
                                           null=True)
    opponent_entry = models.OneToOneField(Entry, on_delete=models.DO_NOTHING, related_name="round_opponent", null=True)

    @property
    def start_time(self):
        return self.battle.start_time + (self.index * self.battle.round_length)

    @property
    def end_time(self):
        return self.start_time + self.battle.round_length

    @property
    def score(self):
        opponent_votes = self.votes.filter(favors="opponent").count()
        contender_votes = self.votes.filter(favors="contender").count()
        draw_votes = self.votes.filter(favors="draw").count()
        return (opponent_votes + draw_votes, contender_votes + draw_votes)

class Battle(models.Model):
    status = models.CharField(max_length=32, choices=BATTLE_STATUS_CHOICES, default="awaiting_approval")
    contender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="contender_battles")
    opponent = models.ForeignKey(User, on_delete=models.CASCADE, related_name="opponent_battles")
    start_time = models.DateTimeField(default=timezone.now)
    round_length = models.DurationField(default=timedelta(days=3))

还有序列化器:

class RoundSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.IntegerField(source="pk", read_only=True)

    class Meta:
        model = Round
        fields = ["id", "battle", "index", "contender_entry", "opponent_entry", "start_time", "end_time", "score"]
        read_only_fields = ["id", "battle", "index", "start_time", "end_time", "score"]

class BattleSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.IntegerField(source='pk', read_only=True)
    current_round = RoundSerializer(read_only=True)

    class Meta:
        model = Battle
        fields = ["url", "id", "status", "start_time", "round_length",
                  "opponent", "contender", "rounds", "current_round"]
        read_only_fields = ["contender", "rounds", "status"]


class BattleReadSerializer(BattleSerializer):
    contender = UserSerializer(read_only=True)
    opponent = UserSerializer(read_only=True)

    class Meta:
        model = Battle
        fields = ["url", "id", "status", "start_time", "round_length",
                  "opponent", "contender", "rounds", "current_round"]
        read_only_fields = ["contender", "rounds"]
        depth = 1

请注意,我有 2 个 Battle 序列化程序:BattleSerializer 用于 POST 和 PUT 以及用户超链接而不是嵌套字段。 BattleReadSerializer 用于 GET 并嵌套输出。 BattleReadSerializer 是上面示例中使用的那个。

我已经尝试将字段显式添加到RoundSerializer,如下所示:

class RoundSerializer(serializers.HyperlinkedModelSerializer):
    id = serializers.IntegerField(source="pk", read_only=True)
    start_time = serializers.DateTimeField(read_only=True)
    score = serializers.ListField(read_only=True)

但这并没有改变任何东西。有没有办法让属性字段显示(除了让它们成为 db 字段并经常重新计算它们)?

【问题讨论】:

    标签: python django django-models django-rest-framework django-serializer


    【解决方案1】:

    尝试将 RoundSerializer 序列化程序显式添加到 BattleReadSerializer 序列化程序,如下所示,

    class BattleReadSerializer(BattleSerializer):
        contender = UserSerializer(read_only=True)
        opponent = UserSerializer(read_only=True)
        rounds = RoundSerializer(read_only=True, many=True)
    
        class Meta:
            model = Battle
            fields = ["url", "id", "status", "start_time", "round_length",
                      "opponent", "contender", "rounds", "current_round"]
            read_only_fields = ["contender", "rounds"]
            depth = 1  # remove this

    【讨论】:

      猜你喜欢
      • 2020-07-11
      • 2017-04-08
      • 2017-04-09
      • 1970-01-01
      • 1970-01-01
      • 2016-01-22
      • 2013-02-26
      • 1970-01-01
      • 2021-11-30
      相关资源
      最近更新 更多