在 Django 中直接访问外键 ID 字段更快吗?
Is accessing foreign key ID field directly faster in Django?
请参考这些示例模型:
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)
I am trying to figure out if the following is faster than the alternative
否。它将导致 same 查询。除了 Django 花几个周期检查您传递的内容和访问主键外,它没有任何区别。
您可以在 The 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
中的 Player
,您可以使用 .prefetch_related(…)
[Django-doc] 来获取一个 Player
的所有 Player
set 个团队在一个查询中,避免 N+1 问题:
teams = Team.objects.<b>prefetch_related('player_set')</b>
在这里,如果您随后遍历 teams
,并获取每个 Team
对象的 player_set
,它不会进行额外的查询,因为它已经获取了所有相关的Player
s 在一个额外的查询中,并在 Python/Django 层做了 "joining"。
所以为了更好地说明我在评论中的观点 - 我在野外经常看到的是 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 %}
请参考这些示例模型:
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)
I am trying to figure out if the following is faster than the alternative
否。它将导致 same 查询。除了 Django 花几个周期检查您传递的内容和访问主键外,它没有任何区别。
您可以在 The 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
中的 Player
,您可以使用 .prefetch_related(…)
[Django-doc] 来获取一个 Player
的所有 Player
set 个团队在一个查询中,避免 N+1 问题:
teams = Team.objects.<b>prefetch_related('player_set')</b>
在这里,如果您随后遍历 teams
,并获取每个 Team
对象的 player_set
,它不会进行额外的查询,因为它已经获取了所有相关的Player
s 在一个额外的查询中,并在 Python/Django 层做了 "joining"。
所以为了更好地说明我在评论中的观点 - 我在野外经常看到的是 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 %}