django中多参数过滤器和链过滤器的区别

iaqfqrcu  于 2022-11-26  发布在  Go
关注(0)|答案(9)|浏览(272)

在django中,多参数过滤器和链式过滤器有什么区别?

20jt8wwn

20jt8wwn1#

正如您在生成的SQL语句中所看到的,不同之处并不在于“OR”,而在于WHERE和JOIN的放置方式。
范例1(相同的链接数据表):来自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)

这将给予你所有的博客,有一个条目与(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)
不同之处在于,它还将为您提供以下结果:
一个博客包含多个条目

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

当评估第一个筛选器时,由于第一个条目而包含图书(即使它具有其他不匹配的条目)。当评估第二个筛选器时,由于第二个条目而包含图书。

**一个表:**但是如果查询不涉及连接表,如Yuji和DTing的示例。结果是相同的。

t1rydlwq

t1rydlwq2#

“多参数筛选查询”的结果不同于“链接筛选查询”的情况如下:
基于引用对象和关系选择被引用对象是一对多(或多对多)的。
多个筛选器:

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

链接筛选器:

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

两个查询可以输出不同的结果:
如果引用模型Referencing1中的多行可以引用被引用模型Referenced中的同一行,则Referenced中可能出现这种情况:Referencing1具有1:N(一对多)或N:M(多对多)关系。
示例:
考虑我的应用程序my_company有两个模型EmployeeDependentmy_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'的子代在学院和学校中都有区别标记。关系代数:
员工****(学校标志〉=75 AND大学标志〉=75)家属
在Second中,如果我需要查询:
是否查找其部分家属在大学和学校中具有特殊标记的所有员工?

>>> 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>]

结果相同!关系代数可以是:
(雇员****(学校标志〉=75)家属)****(大学标志〉=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)

输出相同的结果:x1米15英寸
我检查Django使用print qd1.queryprint qd2.query生成的目标SQL查询是否相同(Django 1.6)。
但是从语义上来说,两者对 me 来说是不同的。第一个看起来像简单的section σ[school_mark〉= 75 AND college_mark〉= 75](Dependent),第二个像缓慢的嵌套查询:σ[学校分数〉= 75](σ[大学分数〉= 75](依赖))。
如果需要Code @codepad
顺便说一句,它是在文档@跨越多值关系我刚刚添加了一个例子,我认为这将是有帮助的人新。

gg58donl

gg58donl3#

大多数情况下,一个查询只有一组可能的结果。
在处理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))

欢迎其他例子。

p1tboqfb

p1tboqfb4#

这个答案基于Django 3.1。

环境

型号

class Blog(models.Model):
    blog_id = models.CharField()

class Post(models.Model):
    blog_id  = models.ForeignKeyField(Blog)
    title    = models.CharField()
    pub_year = models.CharField() # Don't use CharField for date in production =]

数据库表格

过滤呼叫

Blog.objects.filter(post__title="Title A", post__pub_year="2020")
# Result: <QuerySet [<Blog: 1>]>

Blog.objects.filter(post__title="Title A").filter(post_pub_date="2020")
# Result: <QuerySet [<Blog: 1>, [<Blog: 2>]>

说明

在我开始进一步讨论之前,我必须注意到这个答案是基于使用“ManyToManyField”或反向“ForeignKey”来筛选对象的情况。
如果您使用相同的数据表或“OneToOneField”来筛选对象,则使用“多重参数筛选”或“筛选链接”之间没有差异。它们的运作方式都类似于“AND”条件筛选。
了解如何使用“多参数筛选器”和“筛选器链”的直接方法是记住在“ManyToManyField”或反向“ForeignKey”筛选器中,“多参数筛选器”是“AND”条件,而“筛选器链”是“OR”条件。
“多参数过滤器”和“过滤器链”之所以如此不同,是因为它们从不同的连接表中获取结果,并在查询语句中使用不同的条件。
“多参数筛选器”使用**“Post”.“Public_Year”= '2020'**标识公共年份

SELECT *
FROM "Book" 
INNER JOIN ("Post" ON "Book"."id" = "Post"."book_id")
WHERE "Post"."Title" = 'Title A'
AND "Post"."Public_Year" = '2020'

“筛选链”数据库查询使用**“T1”.“Public_Year”= '2020'**标识公共年份

SELECT *
FROM "Book" 
INNER JOIN "Post" ON ("Book"."id" = "Post"."book_id")
INNER JOIN "Post" T1 ON ("Book"."id" = "T1"."book_id")
WHERE "Post"."Title" = 'Title A'  
AND "T1"."Public_Year" = '2020'

但为什么不同的条件会影响结果?

我相信我们大多数来到这个页面的人,包括我=],在最初使用“多参数过滤器”和“过滤器链”时都有同样的假设。
我们认为结果应该从一个表中获取,如下面的一个表,它是正确的“多参数过滤器”。所以,如果你使用“多参数过滤器”,你会得到一个结果,如你所期望的。

但在处理“过滤链”时,Django创建了一个不同的查询语句,将上面的表更改为下面的表。此外,由于查询语句的更改,“Public Year”被标识在“T1”部分而不是“Post”部分。

但是这个奇怪的“过滤链”连接表图是从哪里来的呢?

我不是数据库Maven。下面的解释是我创建了数据库的确切结构,并使用相同的查询语句进行了测试后,到目前为止所理解的。
下图将显示这个奇怪的“过滤链”连接表图的来源。

x1c4d 1x指令集
数据库将首先通过逐个匹配“Blog”和“Post”表的行来创建连接表。
之后,数据库现在再次执行相同的匹配过程,但使用步骤1的结果表来匹配“T1”表,该表与“Post”表完全相同。
这就是这个奇怪的“过滤链”连接表图的来源。

结论

因此,有两件事使“多参数筛选器”和“筛选器链”不同。

  1. Django为“Multiple Arguments Filter”和“Filter-chain”创建了不同的查询语句,这使得“Multiple Arguments Filter”和“Filter-chain”的结果来自其他表。
    1.“筛选器链”查询语句标识的条件来自“多参数筛选器”以外的位置。
    记住如何使用它的肮脏方法是**“多参数筛选器”是一个“AND”条件,而“筛选器链”是一个“OR”**条件,而在“ManyToManyField”或反向“ForeignKey”筛选器中。
scyqe7ek

scyqe7ek5#

性能差异是巨大的,试试看吧。
Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)
比之于
Model.objects.filter(condition_a, condition_b, condition_c)
正如在Effective Django ORM中提到的,

  • QuerySet在内存中维护状态
  • 链接触发克隆,复制该状态
  • 不幸的是,QuerySets维护了很多状态,
  • 如果可能,不要链接多个筛选器
vlju58qv

vlju58qv6#

您可以使用连接模块来查看要比较的原始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)
>>>
gstyhher

gstyhher7#

如果你最终在这个页面上寻找如何用多个链接过滤器动态构建一个django queryset,但是你需要过滤器是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)
9jyewag0

9jyewag08#

如果需要a和B,则

and_query_set = Model.objects.filter(a=a, b=b)

如果需要A以及B,则

chaied_query_set = Model.objects.filter(a=a).filter(b=b)

正式文件:https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
相关帖子:Chaining multiple filter() in Django, is this a bug?

tcomlyy6

tcomlyy69#

当您对相关对象有请求时,这是有区别的,例如

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

相关问题