在 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,它不会进行额外的查询,因为它已经获取了所有相关的Players 在一个额外的查询中,并在 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 %}