Django ORM QuerySet由一个字段交集

时间:2011-01-03 11:24:12

标签: sql django django-models django-orm

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

Blog:
  name
  etc...

Article:
  name
  blog
  creator
  etc

User (as per django.contrib.auth)

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

2 个答案:

答案 0 :(得分:2)

这是锻炼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上):

from django.db import models
from django.contrib.auth.models import User

class Blog(models.Model):
    name = models.CharField(max_length=128)

class Article(models.Model):
    name = models.CharField(max_length=128)
    blog = models.ForeignKey(Blog)
    creator = models.ForeignKey(User)

然后是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 app被命名为foo):

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 )

因此,虽然WHERE子句确实有(A AND B),但AB引用了不同的内部联接("foo_article"."creator_id"T4."creator_id") ,而不是相同的表(这是过滤器(A,B)将生成的,(即:WHERE ("foo_article"."creator_id" = 1 and "foo_article"."creator_id" = 2)

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

就像我说的那样,这是一次很棒的ORM练习!

答案 1 :(得分:1)

我记得读过像“Django ORM你有85%的时间在那里,其他15%是raw SQL”这样的内容来自核心团队的人 - 但我找不到来源了。

您的问题似乎适合that 15% of raw SQL