为什么queryset [0]和queryset.first()返回不同的记录? [英] Why do queryset[0] and queryset.first() return different records?

查看:45
本文介绍了为什么queryset [0]和queryset.first()返回不同的记录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我今天发现,我可以通过使用索引(即 queryset [n] )对其进行引用来访问查询集中的元素.但是,在我发现 queryset [0] 不会立即返回与 queryset.first()相同的记录.为什么会这样,并且其中之一更正确"吗?(我知道 .first()更快,但除此之外)

I discovered today that I can access elements in a queryset by referencing them with an index, i.e. queryset[n]. However, immediately after I discovered that queryset[0] does not return the same record as queryset.first(). Why is this, and is one of those more "correct"? (I know that .first() is faster, but other than that)

Python 3.7.4
Django 1.11.20

Python 3.7.4
django 1.11.20

推荐答案

qs [0] qs.first()之间在语义上存在细微差别.如果您自己没有在查询集中指定 的顺序,那么Django将在获取第一个元素之前通过主键对查询集本身进行排序.

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.

此外,如果查询集为 empty ,则 .first()将返回 None .而 qs [0] 将引发 IndexError .

Furthermore .first() will return None if the queryset is empty. Whereas qs[0] will raise an IndexError.

声称 .first()更快,但是不是 True .实际上,如果您使用 qs [ n ] ,那么Django将在幕后通过切片 qs [ n : n +1] ,因此在数据库后端支持的情况下,它将使用 LIMIT 1 OFFSET n 进行查询,从而获取一条记录,就像 .first()一样.如果已经检索到查询集,则由于已经缓存了数据,因此它将不再进行 额外的查询.

The claim that .first() is faster however is not True. 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.

您可以在 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 objects[0]
        return None

如您所见,如果查询集已经排序( self.ordered True ,那么我们采用 self [:1] ,检查是否有记录,如果有,将其返回.如果没有,我们返回 None .

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.

用于检索特定索引处的项目的代码更加含糊.本质上,它将设置从 k k + 1 的限制,具体化该项目,并返回第一个项目,如我们在

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.
        """
        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]

这篇关于为什么queryset [0]和queryset.first()返回不同的记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆