Django中具有多个参数的过滤器和链式过滤器之间的区别


Answers:


59

正如您在生成的SQL语句中看到的那样,差异不是某些人可能怀疑的“ OR”。这是WHERE和JOIN放置的方式。

Example1(相同的联接表):来自https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

Blog.objects.filter(
       entry__headline__contains='Lennon', 
       entry__pub_date__year=2008)

这将为您提供所有两个条目都具有一个条目的Blog (entry__headline__contains='Lennon') AND (entry__pub_date__year=2008),这是您从该查询中获得的期望。

结果:

Blog with {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}

示例2(链接)

Blog.objects.filter(
       entry__headline__contains='Lennon'
           ).filter(
       entry__pub_date__year=2008)

这将涵盖示例1的所有结果,但将产生更多结果。因为它首先使用(entry__headline__contains='Lennon')结果过滤器过滤所有博客,然后使用结果过滤器过滤所有博客(entry__pub_date__year=2008)

不同之处在于它还会为您提供以下结果:

具有多个条目的单个Blog

{entry.headline: '**Lennon**', entry.pub_date: 2000}, 
{entry.headline: 'Bill', entry.pub_date: **2008**}

评估第一个过滤器时,由于包含第一个条目,因此将其包括在内(即使该条目的其他条目不匹配也是如此)。当评估第二个过滤器时,由于有第二个条目,所以将这本书包括在内。

一个表:但是,如果查询不涉及联接表,例如Yuji和DTing中的示例。结果是一样的。


20
我认为今天早上我很忙,但是这句话使我感到困惑:“因为它首先过滤所有博客,(entry__headline__contains='Lennon')然后从结果过滤器中过滤(entry__pub_date__year=2008)”“如果“然后从结果中”是准确的,为什么要在其中包含一些内容entry.headline == 'Bill'。 。不会entry__headline__contains='Lennon'筛选出Bill实例吗?
达斯汀·怀亚特

7
我也很困惑。似乎这个答案是错误的,但是它有37个赞成票...
Personman

1
该答案具有误导性和混乱性,请注意,仅当使用Yuji答案中提到的使用M2M关系进行过滤时,以上内容才是正确的。关键是该示例使用每个过滤器语句而不是Entry项来过滤Blog项。
播音员'18年

1
因为每个博客可能有多个条目。语言是正确的。如果您不牢记所有动静的概念,则可能会造成混淆。
DylanYoung

@DustinWyatt我也和你有同样的问题,但我终于明白了!请在此页面下面看到由Grijesh Chauhan编写的Employee and Dependent示例,您也将获得它。
–QuestionMan

33

“多个参数filter-query”的结果与“ chained-filter-query”的结果不同的情况如下:

基于引用对象和关系来选择引用对象是一对多(或多对多)的。

多个过滤器:

    Referenced.filter(referencing1_a=x, referencing1_b=y)
    #  same referencing model   ^^                ^^

链接的过滤器:

    Referenced.filter(referencing1_a=x).filter(referencing1_b=y)

两种查询都可以输出不同的结果:
如果引用模型Referencing1中的一行多,则可以引用referenced-model中的同一行Referenced。在以下情况中可能是这种情况ReferencedReferencing1具有1:N(一对多)或N:M(多对多)关系。

例:

考虑我的申请my_company有两种型号Employee,并Dependent。的雇员my_company可以有多个受抚养人(换句话说,一个受抚养人可以是一个雇员的儿子/女儿,而一个雇员可以有多个儿子/女儿)。
恩,假设丈夫和妻子都不能在家里工作my_company。我以1:m为例

因此,Employee被引用的模型可以比被Dependent引用的模型更多。现在考虑如下的关系状态:

Employee:        Dependent:
+------+        +------+--------+-------------+--------------+
| name |        | name | E-name | school_mark | college_mark |
+------+        +------+--------+-------------+--------------+
| A    |        | a1   |   A    |          79 |           81 |
| B    |        | b1   |   B    |          80 |           60 |
+------+        | b2   |   B    |          68 |           86 |
                +------+--------+-------------+--------------+  

依赖a1是指雇员A,依赖是指b1, b2雇员B

现在我的查询是:

是否找到所有拥有儿子/女儿在大学和学校都有显着标记(例如> = 75%)的雇员?

>>> Employee.objects.filter(dependent__school_mark__gte=75,
...                         dependent__college_mark__gte=75)

[<Employee: A>]

输出是'A'依赖的'a1'在大学和学校都有区别标记,这取决于雇员'A'。注意未选择“ B”,因为“ B”的孩子的下乡在大学和学校中都有区分标记。关系代数:

雇员(school_mark> = 75和college_mark> = 75)相关

在第二种情况下,我需要一个查询:

查找所有在大学和学校中其受抚养人具有显着标记的雇员吗?

>>> Employee.objects.filter(
...             dependent__school_mark__gte=75
...                ).filter(
...             dependent__college_mark__gte=75)

[<Employee: A>, <Employee: B>]

这次也选择了“ B”,因为“ B”有两个孩子(一个以上!),一个在学校“ b1”中具有区分标记,另一个在大学“ b2”中具有区分标记。
过滤器的顺序无关紧要,我们也可以将上面的查询写为:

>>> Employee.objects.filter(
...             dependent__college_mark__gte=75
...                ).filter(
...             dependent__school_mark__gte=75)

[<Employee: A>, <Employee: B>]

结果是一样的!关系代数可以是:

(员工(school_mark> = 75)相关)(college_mark> = 75)相关

注意以下内容:

dq1 = Dependent.objects.filter(college_mark__gte=75, school_mark__gte=75)
dq2 = Dependent.objects.filter(college_mark__gte=75).filter(school_mark__gte=75)

输出相同的结果: [<Dependent: a1>]

我检查了Django使用生成的目标SQL查询print qd1.queryprint qd2.query两者都相同(Django 1.6)。

但是从语义上来说我都不一样。第一个看起来像简单的部分σ [school_mark> = 75 AND college_mark> = 75](从属),第二个看起来像慢嵌套查询:σ [school_mark> = 75](σ [college_mark> = 75](从属))。

如果需要Code @codepad

顺便说一句,它在文档中给出@跨越多值关系我刚刚添加了一个示例,我认为这对新人会有所帮助。


4
感谢您提供这一有用的解释,它比文档中还不清楚的解释要好。
2014年

1
关于直接过滤依赖者的最后一个标记非常有用。它表明,只有当您经过多对多关系时,结果的变化才最终发生。如果直接查询表,则链接筛选器就像梳理两次一样。
克里斯(Chris)

20

在大多数情况下,查询只有一组可能的结果。

处理m2m时会使用链接过滤器:

考虑一下:

# will return all Model with m2m field 1
Model.objects.filter(m2m_field=1) 

# will return Model with both 1 AND 2    
Model.objects.filter(m2m_field=1).filter(m2m_field=2) 

# this will NOT work
Model.objects.filter(Q(m2m_field=1) & Q(m2m_field=2))

欢迎其他示例。


4
另一个例子:不仅限于m2m,这也可能一对多发生-进行反向查找,例如在ForeignKey上使用
related_name

感谢您的解释!在此之前,我以为最后一个例子和第二个例子是相等的,所以最后一个例子对我不起作用(错误的查询结果),我花了大量时间进行搜索。第二个例子对我很有帮助。同样正如Wim所说的,这在反向的一对多关系中也可以使用,就像我的情况一样。
zen11625 '16

12

性能差异巨大。试试看。

Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)

相比于

Model.objects.filter(condition_a, condition_b, condition_c)

有效Django ORM中所述

  • QuerySet维护内存中的状态
  • 链接触发克隆,复制该状态
  • 不幸的是,QuerySet保持很多状态
  • 如果可能,请勿链接多个过滤器

8

您可以使用连接模块查看要比较的原始sql查询。正如Yuji的解释,在大多数情况下,它们是等效的,如下所示:

>>> from django.db import connection
>>> samples1 = Unit.objects.filter(color="orange", volume=None)
>>> samples2 = Unit.objects.filter(color="orange").filter(volume=None)
>>> list(samples1)
[]
>>> list(samples2)
[]
>>> for q in connection.queries:
...     print q['sql']
... 
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange  AND `samples_unit`.`volume` IS NULL)
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange  AND `samples_unit`.`volume` IS NULL)
>>> 

2

如果您最终在此页面上寻找如何动态构建带有多个链接过滤器的django查询集,但是您需要过滤器的AND类型不是OR,请考虑使用Q对象

一个例子:

# First filter by type.
filters = None
if param in CARS:
  objects = app.models.Car.objects
  filters = Q(tire=param)
elif param in PLANES:
  objects = app.models.Plane.objects
  filters = Q(wing=param)

# Now filter by location.
if location == 'France':
  filters = filters & Q(quay=location)
elif location == 'England':
  filters = filters & Q(harbor=location)

# Finally, generate the actual queryset
queryset = objects.filter(filters)

如果未传递if或elif,则filter变量将为None,然后您将收到TypeError:&不支持的操作数类型:'NoneType'和'Q'。我使用filter = Q()启动了过滤器
cwhisperer 19-4-17


-4

对相关对象的请求有所不同,例如

class Book(models.Model):
    author = models.ForeignKey(Author)
    name = models.ForeignKey(Region)

class Author(models.Model):
    name = models.ForeignKey(Region)

请求

Author.objects.filter(book_name='name1',book_name='name2')

返回空集

并要求

Author.objects.filter(book_name='name1').filter(book_name='name2')

返回同时具有“ name1”和“ name2”的书籍的作者

有关详细信息,参见 https://docs.djangoproject.com/en/dev/topics/db/queries/#s-spanning-multi-valued-relationships


5
Author.objects.filter(book_name='name1',book_name='name2')甚至不是有效的python,而是SyntaxError: keyword argument repeated
wim 2014年

1
book_name到底在哪里定义?您的意思是book_set__name吗?
DylanYoung
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.