python - 使用 Django ORM 从相关表中获取数据
问题描述
使用 Django ORM,如何在不有效地为每条记录单独调用的情况下访问相关表中的数据(或对数据进行冗余非规范化以使其更易于访问)?
假设我有 3 个模型:
class Tournament(models.Model):
name = models.CharField(max_length=250)
active = models.BooleanField(null=True,default=1)
class Team(models.Model):
name = models.CharField(max_length=250)
coach_name = models.CharField(max_length=250)
active = models.BooleanField(null=True,default=1)
class Player(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.DO_NOTHING
)
number = models.PositiveIntegerField()
age = models.PositiveIntegerField()
active = models.BooleanField(null=True,default=1)
请注意,此 Player 模型在应用程序中很重要,因为它是大多数模型的主要连接 - 从注册到团队到统计数据到结果到奖品。但是这个 Player 模型实际上并不包含人名,因为该模型包含一个用户字段,该字段是AUTH_USER_MODEL
包含名字/姓氏信息的自定义(“用户”)模型的外键。这允许玩家登录应用程序并执行某些操作。
除了这些基本模型,假设一个玩家可以在不同的锦标赛中为不同的球队效力,我还有一个连接的 ManyToMany 模型:
class PlayerToTeam(models.Model):
player = models.ForeignKey(
Player,
on_delete=models.DO_NOTHING
)
team = models.ForeignKey(
Team,
on_delete=models.DO_NOTHING
)
tournament = models.ForeignKey(
Tournament,
on_delete=models.DO_NOTHING
)
作为我遇到的挑战之一的一个例子,假设我正在尝试创建一个表格,允许教练选择他们的首发阵容。所以我需要我的表格来列出特定锦标赛中特定球队的球员姓名。
给定锦标赛和团队 ID,我可以轻松地拉回必要的 QuerySet 来描述我感兴趣的初始记录。
playersOnTeam = PlayerToTeam.objects.filter(tournament=[Tournament_id]).filter(team=[Team_id])
这将返回球队、锦标赛和球员的 ID(但只有 ID)的 QuerySet。但是,名称数据有两个模型:
PlayerToTeam->[player_id]->Player->[user_id]->User->[first_name] [last_name]
现在,如果我只拉回一条记录,我可以简单地做
onlyPlayerOnTeam = PlayerToTeam.objects.filter(tournament=[Tournament_id]).filter(team=[Team_id]).filter(player=[Player_id]).get()
onlyPlayerOnTeam.player.user.first_name
因此,如果我只需要显示名称,我相信我可以在视图返回中传递 QuerySet 并在模板中循环它并显示我需要的内容。但是当我需要将名称显示为表单的一部分时,我不知道您是否可以做类似的事情。
为了填充表单,我相信我可以遍历初始 QuerySet 并构建一个新的数据结构:
playersOnTeam = PlayerToTeam.objects.filter(tournament=[Tournament_id]).filter(team=[Team_id])
allPlayersData= []
for nextPlayer in playersOnTeam:
playerDetails= {
"player_id": nextPlayer.player.id,
"first_name": nextPlayer.player.user.first_name,
"last_name": nextPlayer.player.user.last_name,
}
allPlayersData.append(playerDetails)
form = StartingLineupForm(allPlayersData)
但是,我担心这会导致每个玩家/用户都调用单独的数据库!
虽然这对于 6-10 名玩家来说可能是可以忍受的,但对于更大的数据集,这似乎不太理想。循环执行每个用户的查询似乎是完全错误的。
此外,令人沮丧的是,使用直接的 SQL 查询就足够简单了:
SELECT User.first_name, User.last_name
FROM PlayerToTeam
INNER JOIN Player ON PlayerToTeam.player_id = Player.id
INNER JOIN User ON Player.user_id = User.id
WHERE PlayerToTeam.tournament_id=[tourney_id] AND PlayerToTeam.team_id=[team_id]
但是我正在尝试尽可能地坚持 Django ORM 最佳实践,并避免在我无法立即弄清楚某些事情时只使用 SQL 查询,而且我几乎可以肯定这并不是那么复杂如果不求助于直接 SQL 查询,我就无法做到这一点。
我开始研究select_related
and prefetch_related
,但是我很难理解这些对于关系的工作原理,而不是单个表连接。好像我可以Player.age
使用预取访问数据,但我不知道如何从中获得User.first_name
。
任何帮助,将不胜感激。
解决方案
- 我建议两种方法:
A)选择相关(一个数据库查询):
objects = PlayerToTeam.objects.filter(
...
).select_related(
'player__user',
).only('player__user__name')
name = objects.first().user.name
B)注释(一个数据库查询):
objects = PlayerToTeam.objects.filter(
...
).annotate(
player_name=F('player__user__name'),
)
name = objects.first().player_name
- 为确保您只有一个特定对象
player
,team
并且tournament
,我建议添加unique_together
:
class PlayerToTeam(models.Model):
...
class Meta:
unique_together = ('player', 'team', 'tournament', )
推荐阅读
- python - 绘制一张图表,计算每天的出行次数
- angular - 如何在 Angular 中创建与另一个组件关联的我自己的组件生命周期回调?
- php - PHP 语法 ?>
我无法弄清楚 ?> <? 此代码中正在执行语法。我已经搜索了 php 语法页面,这不是一个开始或结束标记,也不是一个简短的打开标记。没有它,代码将无法编译,并且我在后面的大括号中得到了一种奇怪的颜色。
if(isset($_POST)){?> <form method="POST" action="Form_Index.php">
- android - 在同步函数中等待异步结果
- sql-server - Get-SqlErrorLog 电子邮件备份失败
- html - 如何从 html 文档中批量删除此处的整个文本
- flutter - 当您返回更改第一个下拉菜单时,取决于 Dropdown 失败断言错误
- python - 为什么我实现的mergeSort有O(n^2)?
- flutter - 如何在 Flutter 中使用 ListTile 显示列内的项目列表
- gnuradio - gnuradio 标记流到 PDU 块中缺少长度标记