Существует небольшая семантическая разница между qs[0]
и qs.first()
. Если вы не указали порядок в наборе запросов самостоятельно, то Django упорядочит сам набор запросов по первичному ключу, прежде чем извлекать первый элемент.
Кроме того, .first()
вернет None
если набор запросов пуст . Принимая во внимание, что qs[0]
повысит IndexError
.
Заявление о том, что .first()
быстрее, однако составляет , а не True
. Фактически, если вы используете qs[<i>n</i>]
, то Django будет, за кулисами, извлекать запись путем нарезки с помощью qs[<i>n</i>:<i>n</i>+1]
, следовательно, с учетом того, что серверная часть базы данных поддерживает это, сделает запрос с LIMIT 1 OFFSET <i>n</i>
и, таким образом, получит одна запись, как и в случае .first()
. Если набор запросов уже получен, он, кроме того, не будет выполнять никаких дополнительных запросов на всех , поскольку данные уже кэшированы.
Вы можете увидеть реализацию на GitHub :
def first(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 <b>objects[0]</b>
return None
Как видите, если набор запросов уже упорядочен (self.ordered
равен True
, тогда мы берем self[:1]
, проверяем, есть ли запись, и если это так, верните его. Если нет, мы вернем None
.
Код для извлечения элемента по указанному индексу c является более криптовым c. По сути, он устанавливает пределы от k
до k+1
, материализуйте элемент и верните первый элемент, как мы видим в исходном коде [GitHub] :
def __getitem__(self, k):
"""
Retrieves an item or slice from the set of results.
"""
if not isinstance(k, (slice,) + six.integer_types):
raise TypeError
assert ((not isinstance(k, slice) and (k >= 0)) or
(isinstance(k, slice) and (k.start is None or k.start >= 0) and
(k.stop is None or k.stop >= 0))), \
"Negative indexing is not supported."
if self._result_cache is not None:
return self._result_cache[k]
if isinstance(k, slice):
qs = self._clone()
if k.start is not None:
start = int(k.start)
else:
start = None
if k.stop is not None:
stop = int(k.stop)
else:
stop = None
qs.query.set_limits(start, stop)
return list(qs)[::k.step] if k.step else qs
qs = self._clone()
qs.query.set_limits(k, k + 1)
return <b>list(qs)[0]</b>