Sven
Sven

Reputation: 1172

Creating a QuerySet manually in Django from list of ids

Let's say I have a model My_model and I have a list of ids for this model;

my_ids = [3,2,1,42]

Now I want to get a QuerySet with the My_model.objects of the ids. I already found this solution, but it doesn't work for me, because I want to have the objects in the QuerySet in the same order as the ids in my_ids.

How can I turn a list of ids in a QuerySet in Django and Python?

Upvotes: 5

Views: 3158

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477607

It indeed does not care about the order, since the query looks like:

SELECT model.*
FROM model
WHERE model.pk IN (3, 2, 1, 42)

The database can return the values in any order it wants. Depending on the database you use, the indexing mechanism, etc. the order can be "deterministic", but it is even possible that it will return records completely random.

You can order these, with a rather complicates expression:

from django.db.models import Case, IntegerField, Value, When

my_ids = [3,2,1,42]

Model.objects.filter(
    pk__in=my_ids
).order_by(
    Case(
        *[When(pk=pk, then=Value(i)) for i, pk in enumerate(my_ids)],
        output_field=IntegerField()
    ).asc()
)

This is still a QuerySet, and can be used for lazy querying purposes, but the question remains why you want to sort the elements based on the order of the primary keys. Usually the model (or a related model) contains more "interesting" ways to order elements.

Upvotes: 6

Tom Carrick
Tom Carrick

Reputation: 6616

You can use in_bulk():

queryset = Foo.objects.in_bulk(my_ids)

However, this will have the same problem. This isn't a limitation of Django, but of SQL. The database will give you back the data however it wants unless you explicitly order it, and there's not really a good way to arbitrarily order things.

So the answer is, you can't, easily. You could possibly do weird things with CASE in SQL, but this will possibly be slow.

Probably the simplest way is to convert the queryset to a list and then do your custom ordering in Python, something like:

objs = sorted(list(queryset), key=lambda x: my_ids.index(x.id))

Edit: Willem's answer does weird things with case that is probably faster than this, but I still wouldn't want to grab stuff in arbitrary orders if you care about speed.

Upvotes: 3

Related Questions