Django 提供一种强大而又直观的方式来“处理”查询中的关联关系,它在后台自动帮你处理JOIN。 若要跨越关联关系,只需使用关联的模型字段的名称,并使用双下划线分隔,直至你想要的字段:
下面这个例子获取所有Blog 的name 为'Beatles Blog' 的Entry 对象:
>>> Entry.objects.filter(blog__name='Beatles Blog')
这种跨越可以是任意的深度。
它还可以反向工作。若要引用一个“反向”的关系,只需要使用该模型的小写的名称。
下面的示例获取所有的Blog 对象,它们至少有一个Entry 的headline 包含'Lennon':
>>> Blog.objects.filter(entry__headline__contains='Lennon')
如果你在多个关联关系直接过滤而且其中某个中介模型没有满足过滤条件的值,Django 将把它当做一个空的(所有的值都为NULL)但是合法的对象。这意味着不会有错误引发。例如,在下面的过滤器中:
Blog.objects.filter(entry__authors__name='Lennon')
(如果有一个相关联的Author 模型),如果Entry 中没有找到对应的author,那么它将当作其没有name,而不会因为没有author 引发一个错误。通常,这就是你想要的。唯一可能让你困惑的是当你使用isnull 的时候。因此:
Blog.objects.filter(entry__authors__name__isnull=True)
返回的Blog 对象包括author __name 为空的Blog对象,以及author__name不为空但author__name关联的entry __author 为空的对象。如果你不需要后者,你可以这样写:
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
当你基于ManyToManyField 或反向的ForeignKey 来过滤一个对象时,有两种不同种类的过滤器。考虑Blog/Entry 关联关系(Blog 和 Entry 是一对多的关系)。我们可能想找出headline为“Lennon” 并且pub_date为'2008'年的Entry。或者我们可能想查询headline为“Lennon” 的Entry或者pub_date为'2008'的Entry。因为实际上有和单个Blog 相关联的多个Entry,所以这两个查询在某些场景下都是有可能并有意义的。
ManyToManyField 有类似的情况。例如,如果Entry 有一个ManyToManyField 叫做 tags,我们可能想找到tag 叫做“music” 和“bands” 的Entry,或者我们想找一个tag 名为“music” 且状态为“public”的Entry。
对于这两种情况,Django 有种一致的方法来处理filter() 调用。一个filter() 调用中的所有参数会同时应用以过滤出满足所有要求的记录。接下来的filter() 调用进一步限制对象集,但是对于多值关系,它们应用到与主模型关联的对象,而不是应用到前一个filter() 调用选择出来的对象。
这些听起来可能有点混乱,所以希望展示一个例子使它变得更清晰。选择所有包含同时满足两个条件的entry的blog,这两个条件是headline 包含Lennon 和发表时间是2008 (同一个entry 满足两个条件),我们的代码是:
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
从所有的blog模型实例中选择满足以下条件的blog实例:blog的enrty的headline属性值是“Lennon”,或者entry的发表时间是2008(两个条件至少满足一个,也可以同时满足),我们的代码是:
Blog.objects.filter(entry__headline__contains='Lennon').filter( entry__pub_date__year=2008)
假设这里有一个blog拥有一条包含'Lennon'的entries条目和一条来自2008的entries条目,但是没有一条来自2008并且包含"Lennon"的entries条目。第一个查询不会返回任何blog,第二个查询将会返回一个blog。
在第二个例子中, 第一个filter 限定查询集中的blog 与headline 包含“Lennon” 的entry 关联。第二个filter 又 限定查询集中的blog ,这些blog关联的entry 的发表时间是2008。(译者注:难点在如何理解further这个词!)第二个filter 过滤出来的entry 与第一个filter 过滤出来的entry 可能相同也可能不同。每个filter 语句过滤的是Blog,而不是Entry。
注
跨越多值关系的filter() 查询的行为,与exclude() 实现的不同。单个exclude() 调用中的条件不必引用同一个记录。
例如,下面的查询排除headline 中包含“Lennon”的Entry和在2008 年发布的Entry:
Blog.objects.exclude( entry__headline__contains='Lennon', entry__pub_date__year=2008, )
然而,这与使用filter() 的行为不同,它不是排除同时满足两个条件的Entry。为了实现这点,即选择的Blog中不包含在2008年发布且healine 中带有“Lennon” 的Entry,你需要编写两个查询:
Blog.objects.exclude( entry=Entry.objects.filter( headline__contains='Lennon', pub_date__year=2008, ), )
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29096438/viewspace-2146373/ ,如需转载,请注明出处,否则将追究法律责任。