How merge two querysets from same model without ordering them

Question:

I have create two queryset from same model and merge them as a single queryset. But don’t want doing any ordering on new created queryset. I want that my queryset elements listed as how i merged them.

queryset1  = modelA.objects.filter(id=modelB.rel_id)
queryset2  = modelA.objects.exclude(id=modelB.rel_id)
queryset = queryset1 | queryset2

I expect the output of should be

queryset = <QuerySet [<modelA: YY>, <modelA: XX>, <modelA: ZZ>]>

Because modelA: YY is a queryset1 and i merge it as a first element
But result was :

queryset = <QuerySet [<modelA: XX>, <modelA: YY>, <modelA: ZZ>]>

It ordering by modelA id again. Even i don’t set any ordering in modelA and new queryset.

Asked By: Vugar Shikhaliyev

||

Answers:

You should use the union(…) [Django-doc] function here, like:

queryset = modelA.objects.filter(id=modelB.rel_id).union(
    modelA.objects.exclude(id=modelB.rel_id),
    all=True
)

The or | [Django-doc] on the other hand will, as the documentation says:

Combines two QuerySets using the SQL OR operator.

The following are equivalent:

Model.objects.filter(x=1) | Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))

That being said, if you want to specify the modelB.rel_id as last, you can do that as follows:

from django.db.models import BooleanField, ExpressionWrapper, Q

modelA.objects.annotate(
    rel_to_b=ExpressionWrapper(
        Q(id=modelB.rel_id),
        output_field=BooleanField()
    )
).order_by('rel_to_b')

Here we thus annotate the ModelAs with an extra attribute rel_to_b, in case the objects are related to B, that attribute will be True, and since True is ordered later than False, that will be the last row.

This will produce a query that looks like:

SELECT model_a.*,
       model_a.id = modelB.rel_id AS rel_to_b
FROM model_a
ORDER BY rel_to_b ASC
Answered By: Willem Van Onsem
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.