虽然我理解 Django 的 QuerySet 和延迟执行的高级思想,但我在 Django (3.1.2) 源代码中没有看到查询执行是如何触发的(即数据库被命中)。例如,根据 Django 文档,get()
查询似乎会立即到达数据库。但是查看源代码(django.db.models.query
):
def get(self, *args, **kwargs):
"""
Perform the query and return a single object matching the given
keyword arguments.
"""
clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
if self.query.can_filter() and not self.query.distinct_fields:
clone = clone.order_by()
limit = None
if not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit:
limit = MAX_GET_RESULTS
clone.query.set_limits(high=limit)
num = len(clone)
if num == 1:
return clone._result_cache[0]
if not num:
raise self.model.DoesNotExist(
"%s matching query does not exist." %
self.model._meta.object_name
)
raise self.model.MultipleObjectsReturned(
'get() returned more than one %s -- it returned %s!' % (
self.model._meta.object_name,
num if not limit or num < limit else 'more than %s' % (limit - 1),
)
)
我没有看到clone
(这是一个QuerySet
对象)将其query
发送到数据库引擎的位置(即包含被解析为 SQL 的实际Query
对象的self._query
)并以某种方式将结果缓存在_result_cache
中。事实上,当我尝试单步执行代码时,在某些时候,self._result_cache
"神奇地"填充了查询结果。我从未涉足任何与SQL相关的代码,例如django.db.models.sql.compiler
中的results_iter()
或execute_sql()
,我认为必须调用这些代码才能与数据库后端进行交互?我的猜测是 Django 正在运行不同的进程/线程,而 PyCharm 只通过主线程?如果是这种情况,有人可以指出一些相关的代码吗?我自己似乎无法从谷歌那里找到任何帮助。谢谢!
编辑: 我从代码中知道_fetch_all()
可以触发数据库命中,因为它连接到ModelIterable
,执行SQL命令并将结果处理成Python对象,但是为什么我从来没有进入过这种方法?我想更好的问题是,_fetch_all()
什么时候打电话?
从您添加的代码中,请参阅以下行:
num = len(clone)
可以看到我们正在对查询集调用len
。根据何时评估 QuerySets - Django 文档:
伦().当你对QuerySet调用len()时,会对其进行计算。如您所料,这将返回结果列表的长度。
进一步查看源代码 - GitHublen
会调用_fetch_all()
,因为您正确地假设结果来自哪里:
def __len__(self): self._fetch_all() return len(self._result_cache)
上面提到的文档还记录了何时准确计算查询集。