Django:是否可以在缓存的QuerySet上执行查询而不是查询数据库?

时间:2017-08-31 11:40:13

标签: python django database caching

我经常需要缓存结果并执行类似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不支持这个,为什么呢?也许我的情景不常见?但我有点怀疑,因为我每天偶然发现类似情况。

3 个答案:

答案 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)

When QuerySets are evaluated

在内部,可以构造,过滤,切片并一般传递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()会更有效。