在 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>

如果您需要访问一组Teams 中的Players,您可以使用.prefetch_related(…) [Django-doc] 为一组 获取所有Players在一个查询中,避免 N+1 问题:

teams = Team.objects.<b>prefetch_related('player_set')</b>

如果您随后遍历teams,并获取每个Team 对象的player_set,它不会进行额外的查询,因为它已经在一个额外的查询中获取了所有相关的Players ,并在 Python/Django 层“加入”。

【讨论】:

【参考方案2】:

所以为了更好地说明我在 cmets 中的观点 - 我在野外经常看到的是这样的 url 结构:

/teams/&lt;int:pk&gt;/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 字段是不是更快?的主要内容,如果未能解决你的问题,请参考以下文章

直接在视图Django中设置字段外键值

如何在使用 AJAX、Django REST API 和 jQuery 以模式形式更新记录时显示外键字段名称而不是 ID

在 Django 中从外键设置会话变量:需要字段名而不是主键

Django中的bug总结

姜戈。从表单对象访问模板中的外键字段

django怎样获得框架自动定义的自增id字段