繁体   English   中英

查询Q()不能作为Django中相关字段的“设置包含”吗?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM