如何在 Django Rest Framework 中显示嵌套对象的属性
How to display properties of a nested object in Django Rest Framework
我正在使用 Django 和 Django-Rest-Framework 为战斗系统构建 API。在我的代码中,我有 2 个模型:父模型 Battle
和子模型 Round
。 Round
有一些@属性字段(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
路由时,嵌套的 Round
被返回,但是 仅 数据库字段,而不是属性:
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)
但这并没有改变任何事情。有什么方法可以显示 属性 字段(除了让它们成为数据库字段并经常重新计算它们之外)?
尝试将 RoundSerializer
序列化程序显式添加到 BattleReadSerializer
序列化程序,如下所示,
class BattleReadSerializer(BattleSerializer):
contender = UserSerializer(read_only=True)
opponent = UserSerializer(read_only=True)
<b>rounds = RoundSerializer(read_only=True, many=True)</b>
class Meta:
model = Battle
fields = ["url", "id", "status", "start_time", "round_length",
"opponent", "contender", "rounds", "current_round"]
read_only_fields = ["contender", "rounds"]
<b><strike>depth = 1</strike> # remove this</b>
我正在使用 Django 和 Django-Rest-Framework 为战斗系统构建 API。在我的代码中,我有 2 个模型:父模型 Battle
和子模型 Round
。 Round
有一些@属性字段(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
路由时,嵌套的 Round
被返回,但是 仅 数据库字段,而不是属性:
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)
但这并没有改变任何事情。有什么方法可以显示 属性 字段(除了让它们成为数据库字段并经常重新计算它们之外)?
尝试将 RoundSerializer
序列化程序显式添加到 BattleReadSerializer
序列化程序,如下所示,
class BattleReadSerializer(BattleSerializer):
contender = UserSerializer(read_only=True)
opponent = UserSerializer(read_only=True)
<b>rounds = RoundSerializer(read_only=True, many=True)</b>
class Meta:
model = Battle
fields = ["url", "id", "status", "start_time", "round_length",
"opponent", "contender", "rounds", "current_round"]
read_only_fields = ["contender", "rounds"]
<b><strike>depth = 1</strike> # remove this</b>