Django querySet union?

существует ситуация, в которой при поиске пользователем ключевого слова, результатом будет 3 запроса

a=model.objects.filter(icontains=keyword[0])
b= a.filter(icontains=keyword)
c= a.filter(istartswith=keyword)

Я хочу вернуть результат, который объединяет a, b & c. Но условие - порядок должен быть c,b,a и элементы не должны повторяться. Я пробовал использовать объединение, но порядок не правильный.

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

records = query1 | query2

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

records = (query1 | query2).distinct()

Не уверен, что полностью понял вопрос, но должен попробовать:

from django.db.models import Q
queryset = Model.objects.filter(
    Q(icontains=keyword0) & Q(icontains=keyword1) & Q(icontains=keyword2)
)
You want to chain .distinct() [Django-doc] after a chain of .union(...) [Django-doc] calls:
from django.db.models import Q

qs = (
    c.union(b, all=True).union(a, all=True)
    .distinct()
    .order_by('-pk')
)
This will thus generate a query that performs a UNION ALL of c, b and a, and then perform a .distinct() to filter duplicate rows, and order these by primary key in descending order.
Note: It is important to use all=True here, since otherwise Django will perform UNIONs, and discard duplicates, and thus the .distinct() will have no effect.
In case the model has no pk, you can order by a different field, or use .order_by(...).
Note: You can construct the Q objects in a more elegant way with:
Q(field__icontains=keyword) | Q(field__istartswith=keyword)
so:
from django.db.models import Q

qs = Model.objects.filter(
    Q(field__icontains=keyword) | Q(field__istartswith=keyword)
).distinct()
This will thus generate a single query with a UNION of the matches of icontains and istartswith.

Note: Normally a UNION performs a sort-merge operation, and is less efficient than a single query with an OR. Django however optimizes this, and will generate a query with ORs, and a DISTINCT on top, so it is perfectly fine to use .union(...) here
Вернуться на верх