Why Do Queryset[0] And Queryset.first() Return Different Records?
Solution 1:
There is a small semantical difference between qs[0]
and qs.first()
. If you did not specify an order in the queryset yourself, then Django will order the queryset itself by primary key before fetching the first element.
Furthermore .first()
will return None
if the queryset is empty. Whereas qs[0]
will raise an IndexError
.
The claim that .first()
is faster however is notTrue
. In fact if you use qs[n]
, then Django will, behind the curtains fetch the record by slicing with qs[n:n+1]
, hence it will, given the database backend supports this, make a query with LIMIT 1 OFFSET n
, and thus fetch one record, just like .first()
will do. If the queryset is already retrieved, it will furthermore make no extra queries at all, since the data is already cached.
You can see the implementation on GitHub:
deffirst(self):
"""
Returns the first object of a query, returns None if no match is found.
"""
objects = list((self if self.ordered else self.order_by('pk'))[:1])
if objects:
return objects[0]
returnNone
As you can see, if the queryset is already ordered (self.ordered
is True
, then we take self[:1]
, check if there is a record, and if so return it. If not we return None
.
The code for retrieving an item at a specific index is more cryptic. It essentially will set the limits from k
to k+1
, materialize the item, and return the first item, as we can see in the source code [GitHub]:
def__getitem__(self, k):
"""
Retrieves an item or slice from the set of results.
"""ifnotisinstance(k, (slice,) + six.integer_types):
raise TypeError
assert ((notisinstance(k, slice) and (k >= 0)) or
(isinstance(k, slice) and (k.start isNoneor k.start >= 0) and
(k.stop isNoneor k.stop >= 0))), \
"Negative indexing is not supported."if self._result_cache isnotNone:
return self._result_cache[k]
ifisinstance(k, slice):
qs = self._clone()
if k.start isnotNone:
start = int(k.start)
else:
start = Noneif k.stop isnotNone:
stop = int(k.stop)
else:
stop = None
qs.query.set_limits(start, stop)
returnlist(qs)[::k.step] if k.step else qs
qs = self._clone()
qs.query.set_limits(k, k + 1)
returnlist(qs)[0]
Post a Comment for "Why Do Queryset[0] And Queryset.first() Return Different Records?"