且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Django中相关字段的查询Q()不能作为'set contains'?

更新时间:2023-02-27 13:16:31

通过打印Q&操作,我已经找到了为什么Q正在这样工作的原因。

 从django.db导入连接
打印connection.queries
u'SELECT DISTINCTmarket_basketid,market_basket。weightFROMmarket_basketINNER JOINmarket_fruitON(market_basketid=market_fruit。 (market_fruitname,id)WHERE(market_fruitname,nameIN(\'apple\')ANDmarket_fruitname)INNER JOINmarket_fruitnameON(market_fruitofkind_id=market_fruitname nameIN(\'banana\'))LIMIT 21'

关键问题是在单个过滤器中使用查询时, WHERE 子句将不会在单个条件下满足。它实际上是寻找一个 fruitname 是在['apple']和['banana'],这是不可能的。需要的是找到(那些具有苹果水果名称的水果)或(具有香蕉果实的水果)



目前唯一可行的解​​决方案是链式过滤器。


Let's say we have baskets of fruits. How to filter out those baskets that contains all fruits in a given basket?

In this document https://docs.djangoproject.com/en/dev/ref/models/querysets/#in 'in' method seems will return any basket which contains any of the given fruit. Is there any "set_contains" method to be used for filtering ? Such as Basket.objects.filter(fruit_set_containsAll=fruitSet) ?

Edit: I found that Q() is not working as expected. I'll post the test here:

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)

the database is set as 'apple' only in 1 basket, 'pear' in 2 baskets, and 'banana' in 3 basket:

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

If I try to query with 'apple' and 'banana', it gives empty set !!

print Basket.objects.filter(Q(fruit__ofkind__name__in=['apple'])&Q(fruit__ofkind__name__in=['banana'])).distinct()
[]

similarly,

print Basket.objects.filter(Q(fruit__ofkind__name__in=['banana'])&Q(fruit__ofkind__name__in=['pear'])).distinct()
[]

Here's how I use 'in' to filter, it is not what I need which is supposed to be an empty set.

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

The only way that is working properly is chaining with filter:

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

These code is tested with Django 1.9.4 Any explaining? I would undo the accepted answer for the moment.

By printing the raw SQL of the Q & operations, I've found the reason why Q is working as this.

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'

the key problem is WHERE clause will not be satisfied in a single condition when queries is used in a single filter. It is actually looking for a fruitname is both in ['apple'] and ['banana'] which is not possible. What is needed is to find (those fruits that have fruitname of 'apple') or (those fruits that have fruitname of 'banana')

Currently the only viable solution is to chain filters.