1

考虑以下代码:

questions = Question.objects.only('id', 'pqa_id', 'retain')
del_questions = questions.filter(retain=False)
# Some computations on del_questions
del_questions.delete()
add_questions = questions.filter(pqa_id=None)

不会add_questions包含问题retain=False?即当我们在其子集上questions运行时是否重新评估对象?delete()del_questions

4

1 回答 1

3

简短回答:您在这里使用不同QuerySet的 s,因此您将在这里通过创建副本进行另一个查询。如果你使用相同的,Django 将删除缓存,QuerySet因此它将重新评估. 然而,由于缓存在另一个之前评估QuerySet的对象中,因此可以让对象暂时在调用中幸存下来。.delete()QuerySet

当我们在其子集 del_questions 上运行时,是否会questions重新评估对象delete()

questionss 从一开始就不会被评估。AQuerySet是可迭代的,如果您对其进行迭代(或获取长度或其他内容),将导致查询。但是,如果您编写Model.objects.all().filter(foo=3),那么 Django 将不会.all()首先通过将所有对象提取Model到内存中来“评估” 。

AQuerySet本质上是一个构建查询的工具,通过链接操作和每次构建一个的查询集。最终,您可以评估其中一个查询集。

在这里通过应用一个.filter(..)为两个调用。因此,我们构建了两个不同 QuerySet的 s,因此如果您评估前者,那么这不会导致后者中的任何缓存。

第二个重要的注意事项是 a.delete()不评估查询集,因此不缓存结果如果我们检查.delete()方法 [GitHub],我们会看到:

def delete(self):
    """Delete the records in the current QuerySet."""
    assert self.query.can_filter(), \
        "Cannot use 'limit' or 'offset' with delete."

    if self._fields is not None:
        raise TypeError("Cannot call delete() after .values() or .values_list()")

    del_query = self._chain()

    # The delete is actually 2 queries - one to find related objects,
    # and one to delete. Make sure that the discovery of related
    # objects is performed on the same database as the deletion.
    del_query._for_write = True

    # Disable non-supported fields.
    del_query.query.select_for_update = False
    del_query.query.select_related = False
    del_query.query.clear_ordering(force_empty=True)

    collector = Collector(using=del_query.db)
    collector.collect(del_query)
    deleted, _rows_count = collector.delete()

    # Clear the result cache, in case this QuerySet gets reused.
    self._result_cache = None
    return deleted, _rows_count

使用self._chain(),可以创建 querset 的副本。所以即使这会改变 a 的状态,QuerySet也不会改变 this 的状态QuerySet

另一个有趣的部分是self._result_cache = None,这里 Django重置了缓存。因此,如果在您调用之前已经对查询集进行了评估.delete()(例如,您在调用之前具体化了查询集.delete()),那么它将删除该缓存。因此,如果您重新评估QuerySet,这将导致另一个查询来获取项目。

但是,在某些情况下,数据仍然可能会过时。例如以下:

questions = Question.objects.all()  # create a queryset
list(questions)                     # materialize the result
questions2 = questions.all()        # create a copy of this queryset
questions2.delete()                 # remove the entries

如果我们现在调用list(questions),我们将获得 的缓存中的元素,并且questions不会失效,因此元素从另一个查询集中“存活” a诀窍)。QuerySet.delete()Questions.objects.all().delete()

于 2019-01-07T22:54:05.050 回答