在 Django 中直接访问外键 ID 字段是不是更快?
Posted
技术标签:
【中文标题】在 Django 中直接访问外键 ID 字段是不是更快?【英文标题】:Is accessing foreign key ID field directly faster in Django?在 Django 中直接访问外键 ID 字段是否更快? 【发布时间】:2020-09-22 19:39:48 【问题描述】:请参考这些示例模型:
class Player(models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE)
class Team(models.Model):
captain = models.ForeignKey(Manager, on_delete=models.CASCADE)
country = models.CharField()
class Manager(models.Model):
networth = models.FloatField()
我试图弄清楚以下是否比替代方案更快(即访问数据库更少):
team = Team.objects.get()
Player.objects.filter(team_id=team.id)
替代方案:
team = Team.objects.get()
Player.objects.filter(team=team)
【问题讨论】:
这将导致 same 查询。除了 Django 花费几个周期检查您传递的内容和访问主键之外,它没有任何区别。 @WillemVanOnsem 那么归结为易读性,您会建议后者吗? 我会使用team.player_set.all()
,但这当然是一种“品味”。
这里真正的优化是不获取团队,而是根据团队参数过滤玩家:Player.objects.filter(team__name="Da Players")
,但如果您仍然需要团队作为对象,请参阅 Willem 的答案。
@Melvyn 很有趣,为什么过滤 team__name 会比 ID 查询快?
【参考方案1】:
我试图弄清楚以下是否比替代方法更快
否。它将导致 same 查询。除了 Django 花费几个周期检查您传递的内容和访问主键之外,它没有任何区别。
你可以在 Django shell 中检查这个:
>>> print(Player.objects.filter(team_id=team.id).query)
SELECT `app_name_player`.`id`, `app_name_player`.`team_id` FROM `app_name_player` WHERE `app_name_player`.`team_id` = 1
>>> print(Player.objects.filter(team=team).query)
SELECT `app_name_player`.`id`, `app_name_player`.`team_id` FROM `app_name_player` WHERE `app_name_player`.`team_id` = 1
所以这两个查询是相同的。使用以下方式获取这些可能更惯用:
team<b>.player_set.all()</b>
如果您需要访问一组Team
s 中的Player
s,您可以使用.prefetch_related(…)
[Django-doc] 为一组组 获取所有Player
s在一个查询中,避免 N+1 问题:
teams = Team.objects.<b>prefetch_related('player_set')</b>
如果您随后遍历teams
,并获取每个Team
对象的player_set
,它不会进行额外的查询,因为它已经在一个额外的查询中获取了所有相关的Player
s ,并在 Python/Django 层“加入”。
【讨论】:
【参考方案2】:所以为了更好地说明我在 cmets 中的观点 - 我在野外经常看到的是这样的 url 结构:
/teams/<int:pk>/players/
附上这样的查看代码:
def players_view(request, pk):
team = Team.objects.get(pk=pk)
context =
"players": Player.objects.filter(team=team)
...
虽然你可以这样做:
def players_view(request, pk):
context =
"players": Player.objects.filter(team__pk=pk)
...
加分:
def players_view(request, pk):
context =
"players": Player.objects.filter(team__pk=pk).select_related("team")
...
还有模板技巧:
% for player in players %
<!-- team name only once -->
% if forloop.first %
<h1>Players of player.team.name</h1>
% endif %
<!-- data of player here -->
% endfor %
【讨论】:
以上是关于在 Django 中直接访问外键 ID 字段是不是更快?的主要内容,如果未能解决你的问题,请参考以下文章
如何在使用 AJAX、Django REST API 和 jQuery 以模式形式更新记录时显示外键字段名称而不是 ID