Unexpected behaviour in django queryset

I have the following code:

foo_list = Foo.objects.all()[100:200]
print foo_list[0].id
print len(foo_list)
print foo_list[0].id

I expect that result of first and third print be eqaul but they are not.How this could be happen?

Upvotes: 2

Views: 116

Answers (2)

Selcuk
Selcuk

Reputation: 59425

The reason is that querysets are lazy.

When you construct your foo_list in the first line, nothing happens on database. The query is not executed until you actually get an object from the queryset.

So, when you get foo_list[0].id for the first time, you actually re-slice it, thus running a SELECT statement asking for a single row. Now, when you call len(foo_list) you force the original queryset (which returns 100 rows) to be executed.

Since you run two different queries (one for a single row, and one for 100 rows) and did not specify an order_by clause, the two queries may (and do) return different results.

Upvotes: 0

Alex Martelli
Alex Martelli

Reputation: 882501

You're running into the "wontfix" issue at https://code.djangoproject.com/ticket/9006 --

LIMIT queries without ORDER BY aren't guaranteed to return consistent result sets

(depending on the underlying DB engine), and that's what your [0] indexing is causing to happen -- because despite the misleading name foo_list is not a list, it's queryset itself! As documented at https://docs.djangoproject.com/en/1.7/ref/models/querysets/ ,

Slicing an unevaluated QuerySet usually returns another unevaluated QuerySet

Solution: make it a list:

foo_list = list(Foo.objects.all()[100:200])

and live happily ever after:-)

Upvotes: 4

Related Questions