【问题标题】:Get foreign key objects in a single query在单个查询中获取外键对象
【发布时间】:2011-02-19 10:51:33
【问题描述】:

我的 Django 代码中有 2 个模型:

class ModelA(models.Model):
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    created_by = models.ForeignKey(User)

class ModelB(models.Model):
    category = models.CharField(max_length=255)
    modela_link = models.ForeignKey(ModelA, 'modelb_link')
    functions = models.CharField(max_length=255)
    created_by = models.ForeignKey(User)

假设 ModelA 有 100 条记录,所有这些记录可能有也可能没有指向 ModelB 的链接

现在说我想获取每个 ModelA 记录的列表以及来自 ModelB 的数据

我愿意:

list_a = ModelA.objects.all()

然后要获取 ModelB 的数据,我必须这样做

for i in list_a:
    i.additional_data = i.modelb_link.all()

但是,这会对 i 的每个实例运行一个查询。从而运行 101 个查询。

有没有什么方法可以在 1 个查询中运行所有这些?或者至少少于 101 个查询。

我试过输入ModelA.objects.select_related().all(),但这似乎没有任何效果。

【问题讨论】:

    标签: python django django-queryset


    【解决方案1】:

    正如 Ofri 所说,select_related 仅适用于正向关系,而不适用于反向关系。

    在 Django 中没有自动遵循反向关系的内置方法,但请参阅 my blog post 了解一种合理有效地执行此操作的技术。基本思想是一次获取每个项目的所有相关对象,然后手动将它们与其相关项目关联 - 这样您就可以在 2 个查询中完成,而不是 n+1 个。

    【讨论】:

      【解决方案2】:

      Django ORM 是个好东西,但有些事情最好手动完成。 您可以在单个查询中导入连接游标并执行原始 sql。

      from django.db import connection
      cur=connection.cursor()
      cur.execute(query)
      rows = cur.fetchall()
      

      您的查询应该类似于 (for MySQL)

      SELECT * FROM appname_modela INNER JOIN appname_modelb ON appname_modela.id=appname_modelb.modela_link_id
      

      【讨论】:

      • 我知道使用原始 sql 时不会返回对象,所以您知道将结果转换为 Django 对象的任何方法
      • 目前你不能在 django 查询集中创建原始 sql 结果
      【解决方案3】:

      .select_related() 不起作用的原因是 .select_related() 用于跟随外键。您的 ModelA 没有 ModelB 的外键。它的 ModelB 具有 ModelA 的外键。 (因此一个 ModelA 实例可以有多个与之相关的 ModelB 实例)。

      您可以使用它在 2 个查询中执行此操作,以及一些 python 代码:

      list_b = ModelB.objects.all()
      list_a = ModelA.objects.all()
      for a in list_a:
          a.additional_data = [b for b in list_b if b.modela_link_id==a.id]
      

      【讨论】:

        【解决方案4】:
        from django.db.models import OuterRef, Subquery
        
        newest = ModelB.objects.filter(modela_link=OuterRef('pk'))
        ModelA.objects.annotate(newest=Subquery(newest))
        

        https://docs.djangoproject.com/en/3.2/ref/models/expressions/#subquery-expressions

        【讨论】:

        • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
        猜你喜欢
        • 2011-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-09
        • 2017-10-03
        相关资源
        最近更新 更多