在Django中链接多个过滤器(),这是一个错误? [英] Chaining multiple filter() in Django, is this a bug?

查看:128
本文介绍了在Django中链接多个过滤器(),这是一个错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我总是假设在Django中链接多个filter()调用总是和在一次调用中收集一样。

 #等价
Model.objects.filter(foo = 1).filter(bar = 2)
Model.objects.filter(foo = 1,bar = 2)

但是我在代码中遇到了一个复杂的查询器,而不是这样的情况



$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $
b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b $ b user = models.OneToOneField(auth.models.User)
vacation = models.BooleanField()
country = models.CharField(max_length = 30)

#不等价!
Book.objects.filter(inventory__user__profile__vacation = False).filter(inventory__user__profile__country ='BR')
Book.objects.filter(inventory__user__profile__vacation = False,inventory__user__profile__country ='BR')

生成的SQL是

  SELECTlibrary_bookidin,library_book,asin,library_book,added,library_book,updatedFROMlibrary_bookINNER JOINlibrary_inventoryON(library_book =library_inventorybook_id)INNER JOINauth_userON(library_inventoryuser_id=auth_userid)INNER JOINlibrary_profileON(auth_userid=library_profile user_id)INNER JOINlibrary_inventoryT5 ON(library_bookid= T5book_id)INNER JOINauth_userT6 ON(T5。user_id= T6id)INNER JOIN __文件T7 ON(T6。id= T7user_id)WHERE(library_profile。vacation= False AND T7country= BR)
SELECTlibrary_bookid library_book。asin,library_b add,library_book。updatedFROMlibrary_bookINNER JOINlibrary_inventoryON(library_bookid=library_inventorybook_id)INNER JOINauth_userON(library_inventory user_id=auth_userid)INNER JOINlibrary_profileON(auth_userid=library_profileuser_id)WHERE(library_profilevacation= False AND带有链接的第一个查询器

filter()调用连接库存模型两次有效地在两个条件之间创建一个OR,而第二个查询器将两个条件并入。我期待第一个查询也会和两个条件。这是预期的行为还是Django中的错误?



相关问题的答案使用.filter()filter()。filter()...在Django?似乎表示两个查询器应该是等价的。

解决方案

我明白的是它们的设计是微不足道的(我肯定可以修正): filter(A,B)将首先根据A进行过滤,然后根据B进行子过滤(即AND情况),而 filter(A).filter(B)将返回与A匹配的行和与B(OR'ing)匹配的潜在不同行。 p>

看看这里的例子:



https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships



特别:


应用单个过滤器()调用中的所有内容同时筛选符合所有这些要求的项目。连续过滤器()调用进一步限制对象集


...



< blockquote>

在第二个例子(filter(A).filter(B))中,第一个过滤器将查询器限制为(A)。第二个过滤器将一组博客进一步限制为(B)的博客。第二个过滤器选择的条目可以与第一个过滤器中的条目相同。



I always assumed that chaining multiple filter() calls in Django was always the same as collecting them in a single call.

# Equivalent
Model.objects.filter(foo=1).filter(bar=2)
Model.objects.filter(foo=1,bar=2)

but I have run across a complicated queryset in my code where this is not the case

class Inventory(models.Model):
    book = models.ForeignKey(Book)

class Profile(models.Model):
    user = models.OneToOneField(auth.models.User)
    vacation = models.BooleanField()
    country = models.CharField(max_length=30)

# Not Equivalent!
Book.objects.filter(inventory__user__profile__vacation=False).filter(inventory__user__profile__country='BR')
Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country='BR')

The generated SQL is

SELECT "library_book"."id", "library_book"."asin", "library_book"."added", "library_book"."updated" FROM "library_book" INNER JOIN "library_inventory" ON ("library_book"."id" = "library_inventory"."book_id") INNER JOIN "auth_user" ON ("library_inventory"."user_id" = "auth_user"."id") INNER JOIN "library_profile" ON ("auth_user"."id" = "library_profile"."user_id") INNER JOIN "library_inventory" T5 ON ("library_book"."id" = T5."book_id") INNER JOIN "auth_user" T6 ON (T5."user_id" = T6."id") INNER JOIN "library_profile" T7 ON (T6."id" = T7."user_id") WHERE ("library_profile"."vacation" = False  AND T7."country" = BR )
SELECT "library_book"."id", "library_book"."asin", "library_book"."added", "library_book"."updated" FROM "library_book" INNER JOIN "library_inventory" ON ("library_book"."id" = "library_inventory"."book_id") INNER JOIN "auth_user" ON ("library_inventory"."user_id" = "auth_user"."id") INNER JOIN "library_profile" ON ("auth_user"."id" = "library_profile"."user_id") WHERE ("library_profile"."vacation" = False  AND "library_profile"."country" = BR )

The first queryset with the chained filter() calls joins the Inventory model twice effectively creating an OR between the two conditions whereas the second queryset ANDs the two conditions together. I was expecting that the first query would also AND the two conditions. Is this the expected behavior or is this a bug in Django?

The answer to a related question Is there a downside to using ".filter().filter().filter()..." in Django? seems to indicated that the two querysets should be equivalent.

解决方案

The way I understand it is that they are subtly different by design (and I am certainly open for correction): filter(A, B) will first filter according to A and then subfilter according to B (i.e. the AND situation), while filter(A).filter(B) will return a row that matches A and a potentially different row that matches B (OR'ing).

Look at the example here:

https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

particularly:

Everything inside a single filter() call is applied simultaneously to filter out items matching all those requirements. Successive filter() calls further restrict the set of objects

...

In this second example (filter(A).filter(B)), the first filter restricted the queryset to (A). The second filter restricted the set of blogs further to those that are also (B). The entries select by the second filter may or may not be the same as the entries in the first filter.`

这篇关于在Django中链接多个过滤器(),这是一个错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆