Очень странное поведение в отношении фильтрации и объединения множества наборов запросов

#python #django

#python #django

Вопрос:

У календарей есть владелец и есть ManyToMany поле «помощники» у меня есть календарь, у которого есть 2 помощника, один из которых является его владельцем. Я думаю, что эти 3 строки кода в оболочке django могут достаточно хорошо объяснить странное поведение.

 In [17]: Calendar.objects.filter(assistants=customer).exclude(owner=customer)                             
Out[17]: <QuerySet []>
In [20]: Calendar.objects.filter(owner=customer)                                                          
Out[20]: <QuerySet [<Calendar: aliz cal>, <Calendar: yassi has a calendar>]>
In [19]: Calendar.objects.filter(owner=customer) | Calendar.objects.filter(assistants=customer).exclude(owner=customer)                                                                                    
Out[19]: <QuerySet [<Calendar: aliz cal>, <Calendar: aliz cal>, <Calendar: yassi has a calendar>]>
 

Конечно, ожидалось, что результатом объединения набора запросов будет их фактическое объединение.

Комментарии:

1. интересно, однако, assistants=customer что причиной этого стала фильтрация набора запросов.

Ответ №1:

Предполагая, что это для django 1.11 :
| не представляет собой профсоюз. Он представляет собой комбинацию OR (которая поддерживает все соединения; следовательно, aliz отображается дважды) из двух наборов запросов.

qs1.filter(x=1) | qs2.exclude(x=1) переводится как:

 SELECT STUFF FROM TABLES_AND_JOINS WHERE (x = 1 OR NOT (x = 1))
 

В то время qs1.filter(x=1).union(qs2.exclude(x=1)) как переводится в:

 SELECT STUFF FROM TABLE1 WHERE x = 1 UNION SELECT STUFF FROM TABLE2 WHERE NOT x = 1
 

Используйте str(qs.query) , чтобы увидеть SQL.

Ответ №2:

То, что вы делаете OR , — WHERE это добавление предложений обоих запросов, что отличается от объединения (и немного сложно при объединении; здесь ORM переключается с внутреннего соединения в наборе запросов # 1 на внешнее соединение в наборе запросов # 3 для учета второго запроса, в котором нет соединений). Смотрите соответствующие документы.

Попробуйте union() , доступно с Django 1.11 и далее:

 qs1 = Calendar.objects.filter(assistants=customer).exclude(owner=customer)                             
qs2 = Calendar.objects.filter(owner=customer) 
qs3 = qs1.union(qs2)