Django 为每个 filter() 调用命中数据库

Posted

技术标签:

【中文标题】Django 为每个 filter() 调用命中数据库【英文标题】:Django hits the database for each filter() call 【发布时间】:2011-09-02 06:45:26 【问题描述】:

我有一些 Django 1.3 代码在循环中查找许多模型实例,即。

my_set = myinstance.subitem_set.all()

for value in values:
  existing = my_set.filter(attr_name=value)
  if len(existing) == 1:
     ...

这可行,但分析 SQL 查询表明它在每次迭代时都会命中数据库。根据https://docs.djangoproject.com/en/1.3/ref/models/querysets/ 迭代相关项目应该急切地加载它们,所以我尝试调用:

list(my_set)

但是,这无济于事。它确实执行了一个查询来加载所有子项,但它仍然对循环内的每个子项进行单独的查询。如何让它使用缓存集而不是每次都命中数据库?数据库是 PostgreSQL 8.4。

【问题讨论】:

【参考方案1】:

问题出在这一行:

if len(existing) == 1:

来自 Django 文档:

len()。当您在其上调用 len() 时,将评估 QuerySet。如您所料,这会返回结果列表的长度。

注意:如果您只想确定集合中的记录数,请不要在 QuerySet 上使用 len()。使用 SQL 的 SELECT COUNT(*) 在数据库级别处理计数要高效得多,而 Django 正是出于这个原因提供了一个 count() 方法。请参阅下面的 count()。

因此,在您的情况下,每次调用 len(existing) 时它都会执行查询。比较有效的方法是:

existing.count() == 1

这也会在您每次调用它时访问数据库,但它会执行SELECT COUNT(*),这样会更快。

【讨论】:

谢谢,但问题的重点是我不希望它每次都访问数据库 - 我希望它将整个集合读入内存并使用它。 那么你不应该依赖 Django QuerySets。做my_set = list(my_set)。您将获得一个可以使用的常规 Python 列表,但在这种情况下您将无法使用 .filter() 和其他 QuerySet 内容。 当然,如果没有内置方法,那么我将不得不这样做(可能是 dict 而不是 list),但我希望 Django 足够聪明处理这个。 @EMP 过滤器转换为由您的数据库执行的 SQL 表达式。您希望 Django 在没有 DB 的情况下处理 SQL? 我希望它能够在不需要数据库时处理没有数据库的方法调用。它使用 SQL 是一个实现细节。

以上是关于Django 为每个 filter() 调用命中数据库的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 中禁用会话创建

在 Django 中使用“.filter().filter().filter()...”有缺点吗?

即使使用 ALLOWED_HOSTS=["*"] 在 django 上使用 debug=False 命中 500 错误

如何通过仅在用户滚动到特定部分时调用它来减少谷歌地图 API 命中数

需要用ajax调用实现django-filter

获取 NumPy 数组中的连续命中数及其第一个/最后一个索引