【问题标题】:Django left outer join with filterDjango用过滤器左外连接
【发布时间】:2013-03-25 18:43:06
【问题描述】:

我正在使用 Django 的内置用户模型,并且有一个自定义 Foo 对象,该对象带有一个外键到用户。我正在寻找适合某些约束的所有 User 对象和所有 Foo 对象,如下所示:

SELECT * from auth_user LEFT OUTER JOIN "foo" ON
(auth_user.id = foo.id AND <other criteria here>)

我应该如何在 Django 中完成这项工作?到目前为止,我已经尝试过:

User.objects.filter(foo__<criteria>)

但生成的 SQL 类似于:

SELECT * from auth_user LEFT OUTER JOIN "foo" ON
(auth_user.id = foo.id) WHERE <other criteria here>

并且仅返回具有符合条件的 Foo 对象的 User 对象。或者,我可以选择所有用户对象并为每个对象运行一个查询,但这会大大降低效率。

【问题讨论】:

  • 其他标准是什么?这一切都还与Foo 模型有关吗?
  • 是的。 Foo 包含几个日期字段,我只从指定的日期范围内选择 Foos。
  • User.objects.filter(foo__&lt;criteria&gt;) 应该做什么? foo 不是 User 模型中的字段。我一直无法生成包含LEFT OUTER JOIN 的查询。

标签: sql django join


【解决方案1】:

要获得LEFT OUTER JOIN,您可以前往:

User.objects.select_related('foo').filter(Q(foo__isnull=True) | Q(<other criteria here>))

Django 使用foo__isnull=True 指示它生成LEFT OUTER JOIN。给foo__isnull=False 以生成INNER JOIN,就像没有过滤器参数一样。

【讨论】:

  • 感谢您添加此内容。
【解决方案2】:

Django 2.0 引入了FilteredRelation objects,它可以生成几乎与您提到的LEFT OUTER JOIN 查询类似的代码:

User.objects.annotate(
    filtered_foo=FilteredRelation('foo', condition=Q(foo_<criteria>))
).values(...) # e.g. 'user__id', 'filtered_foo__id'

但是,您似乎需要通过在 values 中指定或附加注释来明确要求您要使用的 filtered_foo 字段。或者,您还可以聚合按用户分组的 filtered_foo 字段。

【讨论】:

    【解决方案3】:

    如果您希望 Django 获取所有 User 对象和所有与用户对象相关的 Foo 对象,那么您将使用 select_related()

    User.objects.all().select_related('foo')
    

    但在这里您不想要所有与用户对象相关的Foo 对象,您只想要满足您的条件的它们的子集。我不知道如何告诉 Django 在单个查询集中执行此操作。但是您可以做的是分别进行选择并在 Python 中进行连接:

    # Map from user id to corresponding Foo satisfying <criteria>, if any.
    foos = {foo.user_id: foo for foo in 
            Foo.objects.filter(user__isnull = False, <criteria>)}
    for user in User.objects.all():
        foo = foos.get(user.id)
        # ...
    

    (与LEFT OUTER JOIN 相比,这不会做更多的数据库工作或传输更多数据,所以我认为这是一种合理的方法。)

    【讨论】:

    • 请记住,我实际上并不知道您的模型是如何相关的,因此您可能需要稍微调整我提出的解决方案以满足您的需求。
    • 您的解决方案运行此查询: SELECT ••• FROM "auth_user" WHERE "auth_user"."id" = 为每个具有符合条件的 Foo 的用户。据我所知,这与 "{foo.user_id: foo";用其他东西替换任一部分会导致查询不运行。两个模型之间的唯一关系是 Foo 有一个指向 User 的 ForeignKey。
    • 当您创建ForeignKey 字段时,通常会导致将两个属性添加到实例中。例如,如果模型Foo 有一个字段user = ForeignKey(auth_user) 我希望Foo 实例有两个属性:user(如您所说,它发出选择查询以获取相关对象)和user_id (没有)。至少,这是我在运行自己的测试时观察到的。恐怕您必须在这里进行一些自己的调试。
    • 我发现了问题,我之前在 Foo 的 __unicode__(self) 中添加了 user.username。完全是我的错,您的解决方案完美运行。再次感谢!
    • 除非使用此解决方案,您需要遍历所有用户,这并不总是可取的。例如。当您使用 django-endless-pagination 在模板中分页时。
    【解决方案4】:

    这很笨重,但应该这样做:

    User.objects.raw('SELECT auth_user.* from auth_user LEFT OUTER JOIN '
                     '"foo" ON (auth_user.id = foo.id AND '
                     '<other criteria here>)')
    

    您可以在返回的用户对象上添加额外的属性,方法是将它们添加到选择的左侧。

    【讨论】:

      【解决方案5】:

      虽然有很好的答案可以满足一个人的需求,但最好的做法是

      from django.db.models import Prefetch
      
      
      User.objects.all().prefetch_related(Prefetch(<related_name>, queryset=Foo.objects.filter(<criteria>)))
      

      = 'foo' 如果:

      class Foo(models.Model):
          user = models.ForeignKey(User, related_name='foo')
      

      这将返回所有用户和与具有给定过滤条件的用户相关的 foo 对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-10-29
        • 2016-05-08
        • 2016-01-01
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        • 2011-02-01
        相关资源
        最近更新 更多