【问题标题】: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 查询快?

标签: mysql django django-models django-rest-framework mysql-python


【解决方案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 %}
    

    【讨论】:

      猜你喜欢
      • 2011-05-10
      • 2012-08-23
      • 1970-01-01
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 2013-06-08
      • 1970-01-01
      相关资源
      最近更新 更多