Как найти объединение двух наборов запросов Django?

92

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

Есть ли способ получить набор запросов или просто список объектов, представляющий собой объединение наборов запросов, возвращаемых каждым методом менеджера?

Пол Д. Уэйт
источник
3
(Из удаленного ответа) См. Этот вопрос для варианта, который работает с QuerySets из разных моделей: stackoverflow.com/questions/431628/…
rnevius
1
Начиная с версии 1.11, наборы запросов django имеют встроенный метод объединения. Я добавил это в качестве ответа на будущее
Хосе Чериан

Ответы:

179

Это работает и выглядит немного чище:

records = query1 | query2

Если вам не нужны дубликаты, вам нужно будет добавить .distinct():

records = (query1 | query2).distinct()
Джордан Рейтер
источник
5
Хотя принятый ответ возвращает повторяющееся объединение (точнее, список), как и просил OP, этот метод возвращает истинное объединение наборов запросов. С этим набором запросов можно работать и дальше, что желательно во многих случаях.
Кристиан Цибульски
5
Из-за ошибки Django эта конструкция иногда может возвращать неверные результаты при работе с ManyToManyFields. Например, иногда вы видите, что records.count()это будет больше query1.count() + query2.count(), что явно неверно.
Цзянь
4
@Jian, можете ли вы уточнить версию django с ошибкой и ссылкой на проблему djangoproject?
IMFletcher
10
записи = query1 | query2; Records = records.distinct () даст мне правильный результат
Евгений
5
Вы можете перегружать операторы в Python. См. Docs.python.org/2/library/operator.html . Итак, Django создает специальные методы для объекта QuerySet. Смотрите код здесь: github.com/django/django/blob/master/django/db/models/...QuerySet класс предоставляет методы __and__и __or__которые вызываются , когда &или |используются операторы между двумя QuerySetобъектами (также используются для Qкласса , а также ).
Jordan Reiter
49

Начиная с версии 1.11 , наборы запросов django имеют встроенный метод объединения.

q = q1.union(q2) #q will contain all unique records of q1 + q2
q = q1.union(q2, all=True) #q will contain all records of q1 + q2 including duplicates
q = q1.union(q2,q3) # more than 2 queryset union

Дополнительные примеры см. В моем сообщении в блоге .

Хосе Чериан
источник
Я не мог заставить all = True работать. Закончил приведение моего запроса к набору перед его возвратом клиенту.
Брейден Холт
1
@BradenHolt, all = True, означает, что он будет содержать повторяющиеся записи. Вы можете просто удалить all = True, чтобы не передавать его в набор.
Jose Cherian
после этого не работает DjangoFilterBackend, как я могу использовать union и DjangoFilterBackend?
nesalexy
К сожалению, это не работает для моделей с порядком по умолчанию, определенным в мета-мете модели. Всякий раз, когда я пытаюсь объединить их с .union, я получаю следующую ошибку: «ORDER BY не разрешен в подзапросах составных операторов».
jrial
4

Я бы посоветовал использовать query1.union (query2) вместо query1 | query2 '; Я получил разные результаты от двух вышеупомянутых методов, и первый - то, что я ожидал. Вот что я обнаружил:

print "union result:"
for element in query_set1.union(query_set2):
    print element

print "| result:"
for element in (query_set1 | query_set2):
    print element

результат:

union result:
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object

| result:
KafkaTopic object
KafkaTopic object
Сяньсин
источник
1
Пожалуйста, вставьте код, а не изображения кода. Текст в изображениях недоступен для поиска, вы не можете скопировать / вставить его в свой редактор для проверки и занимает больше места, чем необходимо. Используйте обратные кавычки, чтобы пометить код как код, чтобы он был правильно отформатирован. См. Ссылку «справка» рядом с полем ввода текста.
jrial
Спасибо за обновление. :)
jrial