我经常需要缓存结果并执行类似ORM的操作,但使用普通 Python逻辑,因为我不希望数据库因性能问题而被命中。
由于这个原因,我想知道Django是否提供了一种在缓存的QuerySet
上执行ORM操作的方法,而不是每次我们使用的 core 查询稍微改变时查询数据库
为了想象我的意思,让我们说我们有这些不同的问题:
# when evaluated it hits the db
devs = Worker.objects.filter(job__category=JobCategory.DEVELOPER)
# when evaluated it hits the db again
young_devs_salary = devs.filter(dob__gte=datetime.now() - relativedelta(years=24)).values('dob', 'salary')
# it would hit the db again
wellpaid_devs = devs.filter(salary__gte=high_salary_yearly)
请注意,第一个QuerySet
将是以下QuerySet
其余部分的超集。
我正在想办法告诉Django只有第一个查询devs
应该命中db,然后缓存其结果,而其他查询使用{{1} },不应该查询数据库,而是查询缓存的devs
。
这可能吗?如果Django不支持这个,为什么呢?也许我的情景不常见?但我有点怀疑,因为我每天偶然发现类似情况。
答案 0 :(得分:3)
不,它不支持它,我很困惑,你会问为什么不。
查询在数据库中完成。做你要求的就意味着在Python代码中实现该功能的完整副本,这将是低效且不必要的。
答案 1 :(得分:1)
正如丹尼尔已经回答的那样,在没有最终访问数据库的情况下,你不能以这种方式使用查询API。现在你不需要任何特殊的东西来过滤纯python中的查询集结果 - 这主要是使用标准python功能的标准列表过滤 - 但是在这里它实际上不会提高性能,如果有的话。避免无用的数据库命中很好,但这并不意味着数据库是你的敌人,并且在大多数情况下,具有理智模式的经过良好调整的数据库可能会胜过纯python列表过滤。
此外,如果Queryset API支持纯python过滤,那么FWIW将被实现为标准的列表过滤操作等,所以它会给你带来很多东西,恰恰相反。
作为一个例子 - 纯python版本:
young_devs_salary = devs.filter(dob__gte=datetime.now() - relativedelta(years=24)).values('dob', 'salary')
将是:
target_date = datetime.now() - relativedelta(years=24)
young_devs_salary = [(dev.dob, dev.salary) for dev in devs if dev.dob >= target_date]
正如您所看到的,Queryset api中不需要更多内容,但我怀疑这将比查询数据库快得多,除非您的django应用程序与数据库之间的数据集非常小或网络连接非常差服务器(或任何其他基础设施/数据库调优问题)。
答案 2 :(得分:0)
在内部,可以构造,过滤,切片并一般传递QuerySet,而无需实际访问数据库。在您执行评估查询集的操作之前,实际上不会发生数据库活动。
您可以通过以下方式评估QuerySet:
迭代。 QuerySet是可迭代的,并且在您第一次迭代它时执行其数据库查询。例如,这将打印数据库中所有条目的标题:
for e in Entry.objects.all():
print(e.headline)
注意:如果您只想确定是否存在至少一个结果,请不要使用此选项。使用exists()更有效率。
切片。如限制查询集中所述,可以使用Python的数组切片语法对QuerySet进行切片。切片未评估的QuerySet通常会返回另一个未评估的QuerySet,但如果使用切片语法的“step”参数,Django将执行数据库查询,并返回一个列表。切片已经过评估的QuerySet也会返回一个列表。
另请注意,即使切片未评估的QuerySet返回另一个未评估的QuerySet,也不允许进一步修改它(例如,添加更多过滤器或修改排序),因为这不能很好地转换为SQL,并且它不会很清楚意思是。
酸洗/缓存。有关挑选QuerySets时所涉及的内容的详细信息,请参阅以下部分。对于本节而言,重要的是从数据库中读取结果。
再版()。在对它调用repr()时会评估QuerySet。这是 为了方便Python交互式解释器,你可以 在交互使用API时立即看到您的结果。
LEN()。在您上面调用len()时会评估QuerySet。就像你一样 可能期望,返回结果列表的长度。
注意:如果您只需要确定集合中的记录数(并且不需要实际对象),则使用SQL的SELECT COUNT(*)处理数据库级别的计数会更有效。 Django正是出于这个原因提供了count()方法。
列表()。通过调用list()来强制评估QuerySet。对于 例如:
entry_list = list(Entry.objects.all())
BOOL()。在布尔上下文中测试QuerySet,例如使用bool(), 或者,和/或if语句将导致执行查询。如果 至少有一个结果,QuerySet为True,否则为False。 例如:
if Entry.objects.filter(headline="Test"):
print("There is at least one Entry with the headline Test")
注意:如果您只想确定是否存在至少一个结果(并且不需要实际对象),则使用exists()会更有效。