Sql Django ORM QuerySet与字段的交集

Sql Django ORM QuerySet与字段的交集,sql,django,django-models,django-orm,Sql,Django,Django Models,Django Orm,这些是我得到的(伪)模型 Blog: name etc... Article: name blog creator etc User (as per django.contrib.auth) 所以我的问题是:我有两个用户。我想得到两个用户在同一个博客上发表的所有文章(无论是哪个博客)。我不能简单地按两个用户过滤文章模型,因为这将产生由两个用户创建的文章集。显然不是我想要的。但是,我是否可以通过某种方式进行过滤,以获得两个查询集之间对象字段匹配的所有文章?我记得在核心

这些是我得到的(伪)模型

Blog:
  name
  etc...

Article:
  name
  blog
  creator
  etc

User (as per django.contrib.auth)

所以我的问题是:我有两个用户。我想得到两个用户在同一个博客上发表的所有文章(无论是哪个博客)。我不能简单地按两个用户过滤文章模型,因为这将产生由两个用户创建的文章集。显然不是我想要的。但是,我是否可以通过某种方式进行过滤,以获得两个查询集之间对象字段匹配的所有文章?

我记得在核心团队的某个人的演示文稿中读到类似“Django ORM,85%的时间你都在那里,另外15%的时间是在那里”,但我再也找不到来源了


您的问题似乎很合适。

这是一个练习Django的ORM的好问题:-)

根据预先知道的参数,有几种方法可以实现这一点

场景1:您了解用户和特定博客

如果您有一个特定的博客,并且希望简单地查找任何一位作者写的所有文章,那么您可以使用Q对象。我不认为这是你的情况,但我会把它放在这里以防万一:

from django.db.models import Q
Article.objects.filter(Q(creator=user1) | Q(creator=user2), blog=some_blog_instance)
场景2:您只知道用户

如果要查找两个用户都发布过的所有博客,然后查找他们在这些博客中发布了哪些文章,您需要从
Blog
模型开始:

from django.db.models import Q

# Find all blogs where both user1 and user2 have written articles
blogs = Blog.objects.filter(article__creator=user1).\
        filter(article__creator=user2).distinct()

# Now find which articles those were
for blog in blogs:
    articles = blog.article_set.filter(Q=(creator=user1) | Q=(creator=user2))

根据Paulo的评论进行编辑:

下面是一组我认为与OP的伪代码匹配的模型测试集,它演示了上述代码的工作原理(至少在sqlite3和postgres上):

然后是一些user1和user2都在blog2上写文章的数据:

u1 = User.objects.create(username='u1')
u2 = User.objects.create(username='u2')

b1 = Blog.objects.create(name='b1')
b2 = Blog.objects.create(name='b2')
b3 = Blog.objects.create(name='b3')

b1_art1 = Article.objects.create(name='b1_art1', blog=b1, creator=u1)
b2_art1 = Article.objects.create(name='b2_art1', blog=b2, creator=u1)
b2_art2 = Article.objects.create(name='b2_art2', blog=b2, creator=u2)
查询:

[b.name for b in Blog.objects.filter(article__creator=user1).\
    filter(article__creator=user2).distinct()]
产生:

[b2]
SQL是(我的测试django应用程序名为foo):

因此,
WHERE
子句确实有
(A和B)
A
B
引用了不同的内部联接(
“foo_-article”,“creator_-id”
T4。“creator_-id”
),而不是相同的表(这是过滤器(A,B)将生成的表,(即:
WHERE(“foo_-article”,“creator_-id”=1和)“foo_-article”。“创建者id”=2)

(如果没有
distinct()
子句,如果您为其中一位作者添加了更多的文章,您将在queryset结果中获得多个
b2
条目。)


就像我说的,这是一个很好的ORM练习!

我希望避免这种情况,但我认为这是我最有可能的选择。@Sri Raghavan我一开始也是这样。但过了一段时间,我不得不面对它:ORM只用于最简单的查询。当你不得不做一些复杂的事情时,你将不得不使用原始SQL。-1:看起来像
creator=user1和creator=user2将始终为false。@paulo看一看django使用
django.db.connection.querys为
filter().filter()
生成的SQL,它执行两个内部联接(这也是添加
distinct()
的原因),并且不同于
过滤器(creator=user1,creator=user2)
。至少在我的设置(Django 1.2.1和MySQL)中,这会导致一个与
WHERE(filter1和filter2)
的内部连接。对我来说,这似乎是一个逻辑优化……好的,棘手的一个,这就像你说的,取决于FK方向(master.filter(detail)!=detail.filter(master))。无论是否使用distinct,我都得到了相同的结果。这比原始SQL更简单。就个人而言,我有点惊讶blog.filter(article)。filter(article)与article.filter(blog)。filter(blog)。吓人!
[b2]
SELECT "foo_blog"."id", "foo_blog"."name" 
FROM "foo_blog" 
INNER JOIN "foo_article" ON ("foo_blog"."id" = "foo_article"."blog_id") 
INNER JOIN "foo_article" T4 ON ("foo_blog"."id" = T4."blog_id") 
WHERE ("foo_article"."creator_id" = 1  AND T4."creator_id" = 2 )