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 中使用“.filter().filter().filter()...”有缺点吗?
即使使用 ALLOWED_HOSTS=["*"] 在 django 上使用 debug=False 命中 500 错误