views:

526

answers:

2

I fetch the latest 5 rows from a Foo model which is ordered by a datetime field.

qs = Foo.objects.all()[:5]

In the following step, I want to reorder the queryset by some other criteria (actually, by the same datetime field in the opposite direction). But reordering after a slice is not permitted. reverse() undoes the first ordering, giving me a differet queryset. Is there a way to accomplish what I want without creating a list from the queryset and doing the ordering using it?

+3  A: 

No, there's no way of doing that. order_by is an operation on the database, but when you slice a queryset it is evaluated and doesn't go back to the database after that.

Sounds like you already know the solution, though: run reversed() on the evaluated qs.

qs = reversed(Foo.objects.all()[:5])
Daniel Roseman
So I haven't missed anything. Thanks.
shanyu
+3  A: 

order_by gives you SQL in-database ordering. You're already using that, and then slicing on it. At that point, the results are retrieved into memory. If you want to change their order, you need to use Python in-memory sorting to do it, not the ORM in-database sorting.

In your case, Daniel has already given the best solution: since you simply want to sort by the same field, but in the other order, just reverse the list you have:

qs = Foo.objects.all()[:5]
objs = reversed(qs)

If you had wanted to sort by some other field, then you'd use the sorted() function with a custom key function:

qs = Foo.objects.all()[:5]
objs = sorted(qs, key=lambda o: o.some_other_field)
Ned Batchelder
Yes, I know that order_by is a DB ordering. But I have found it interesting that a reverse() (the queryset method) after the slice is working, and that it reexecutes the query, giving unexpected results.
shanyu
When you pull results from a queryset, it is still a valid queryset. If you modify it (for example, by adding reverse()), it will re-evaluate it.
Ned Batchelder