[英]Query Q() is not working as 'set contain' for related field in Django?
假设我们有水果篮。 如何过滤出包含给定篮子中所有水果的那些篮子?
在本文档中, https://docs.djangoproject.com/en/dev/ref/models/querysets/#in'in '方法似乎将返回包含任何给定水果的任何篮子。 是否有任何用于过滤的“ set_contains”方法? 例如Basket.objects.filter(fruit_set_containsAll = fruitSet)吗?
编辑:我发现Q() 不能按预期工作 。 我将在这里发布测试:
class Basket(models.Model):
weight = models.FloatField(default=1)
class Fruitname(models.Model):
name = models.CharField(max_length=32)
class Fruit(models.Model):
ofkind = models.ForeignKey(Fruitname, on_delete=models.CASCADE)
inbasket = models.ForeignKey(Basket, on_delete=models.CASCADE)
weight = models.FloatField(default=1)
数据库仅在1个篮子中设置为“ apple”,在2个篮子中设置为“ pear”,在3个篮子中设置为“ banana”:
print Basket.objects.all()
print Fruitname.objects.all()
[<Basket: id 31 has 4 fruits
1. id - 53 apple(28), weight 1.00
2. id - 54 apple(28), weight 2.00
3. id - 55 apple(28), weight 3.00
4. id - 62 banana(30), weight 10.00
>, <Basket: id 32 has 2 fruits
1. id - 56 pear(29), weight 4.00
2. id - 57 banana(30), weight 5.00
>, <Basket: id 33 has 4 fruits
1. id - 58 pear(29), weight 6.00
2. id - 59 banana(30), weight 7.00
3. id - 60 pear(29), weight 8.00
4. id - 61 pear(29), weight 9.00
>]
[<Fruitname: apple(28)>, <Fruitname: pear(29)>, <Fruitname: banana(30)>]
如果我尝试使用“苹果”和“香蕉”进行查询,则会给出空集!
print Basket.objects.filter(Q(fruit__ofkind__name__in=['apple'])&Q(fruit__ofkind__name__in=['banana'])).distinct()
[]
同样
print Basket.objects.filter(Q(fruit__ofkind__name__in=['banana'])&Q(fruit__ofkind__name__in=['pear'])).distinct()
[]
这是我使用'in'进行过滤的方式,这不是我所需要的,它应该是一个空集。
print Basket.objects.filter(Q(fruit__ofkind__name__in=['apple','pear'])).distinct()
[<Basket: id 31 has 4 fruits
1. id - 53 apple(28), weight 1.00
2. id - 54 apple(28), weight 2.00
3. id - 55 apple(28), weight 3.00
4. id - 62 banana(30), weight 10.00
>, <Basket: id 32 has 2 fruits
1. id - 56 pear(29), weight 4.00
2. id - 57 banana(30), weight 5.00
>, <Basket: id 33 has 4 fruits
1. id - 58 pear(29), weight 6.00
2. id - 59 banana(30), weight 7.00
3. id - 60 pear(29), weight 8.00
4. id - 61 pear(29), weight 9.00
>]
正常工作的唯一方法是与过滤器链接:
Basket.objects.filter(fruit__ofkind__name__in=['apple']).filter(fruit__ofkind__name__in=['banana']).distinct()
[<Basket: id 31 has 4 fruits
1. id - 53 apple(28), weight 1.00
2. id - 54 apple(28), weight 2.00
3. id - 55 apple(28), weight 3.00
4. id - 62 banana(30), weight 10.00
>]
这些代码已经在Django 1.9.4上进行了测试,有什么解释吗? 我现在暂时撤消已接受的答案。
不知道这是否是最高效的方法,但是至少它应该转换为一个(大,丑陋,嵌套的)sql事务(一旦评估了最后一baskets
):
baskets = Basket.objects.all()
for fruit in fruits:
baskets = baskets.filter(id__in=fruit.basket.all())
可以尝试使用一种更优雅(可能更高效)的方法,如下所述,使用Q
对象(基于Dave Webb 对另一个问题的回答 )构建查询:
queries = [Q(id__in=fruit.basket.all()) for fruit in fruits]
query = Q()
# AND the Q object with the ones in the list
for item in queries:
query &= item
baskets = Basket.objects.filter(query)
通过打印Q&操作的原始SQL,我找到了Q如此工作的原因。
from django.db import connection
print connection.queries
u'SELECT DISTINCT "market_basket"."id", "market_basket"."weight" FROM "market_basket" INNER JOIN "market_fruit" ON ("market_basket"."id" = "market_fruit"."inbasket_id") INNER JOIN "market_fruitname" ON ("market_fruit"."ofkind_id" = "market_fruitname"."id") WHERE ("market_fruitname"."name" IN (\'apple\') AND "market_fruitname"."name" IN (\'banana\')) LIMIT 21'
关键问题是,在单个过滤器中使用查询时,无法在单个条件下满足WHERE子句。 它实际上是在['apple']和['banana']中寻找一个水果名称,这是不可能的。 需要找到(水果名称为“苹果”的那些水果)或(水果名称为“香蕉”的那些水果)
当前唯一可行的解决方案是链接过滤器。
要获得一个包含所有可用Fruit
实例的Basket
,您可以执行以下操作:
from django.db.models import Count
# first get all PKs of fruits
fruit_pk_list = Fruit.objects.value_list('id', flat=True)
# Then get filter the basket with all fruits using annotate and Count
baskets = Basket.objects.annotate(
num_fruit=Count('fruit')).filter(num_fruit=len(fruit_pk_list))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.