Sai*_*cko 5 python django django-queryset
今天我发现我可以通过使用索引引用查询集中的元素来访问它们,即queryset[n]. 然而,在我发现它并queryset[0]没有返回与 相同的记录之后queryset.first()。为什么会这样,是其中之一更“正确”吗?(我知道这样.first()更快,但除此之外)
Python 3.7.4
Django 1.11.20
qs[0]和之间存在细微的语义差异qs.first()。如果您自己没有在查询集中指定顺序,那么 Django 将在获取第一个元素之前按主键对查询集本身进行排序。
此外,如果查询集为空,.first()则返回。而将提出.Noneqs[0]IndexError
.first()然而更快的说法却并非如此 True。事实上,如果你使用,那么 Django 将在幕后通过 切片 来获取记录,因此,如果数据库后端支持这一点,它会使用 进行查询,从而获取一条记录,就像会做的那样。如果已经检索到查询集,则它根本不会进行任何额外的查询,因为数据已经被缓存。qs[n]qs[n:n+1]LIMIT 1 OFFSET n.first()
您可以在GitHub上查看实现:
Run Code Online (Sandbox Code Playgroud)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 objects[0] return None
正如你所看到的,如果查询集已经有序(self.orderedis True,那么我们采取self[:1],检查是否有记录,如果有则返回它。如果没有,我们返回None。
用于检索特定索引处的项目的代码更加神秘。k它本质上会设置从到 的限制,具体化该项目,并返回第一个项目,正如我们在源代码 [GitHub]k+1中看到的:
Run Code Online (Sandbox Code Playgroud)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 list(qs)[0]