为何Django关联对象查询未被缓存?重复查询问题解析
Django QuerySet重复查询问题
我编写了如下单元测试:
def test_cache(self): with self.assertNumQueries(1): print(database.records.all()) print(database.records.all()) print(database.records.all())
测试失败,因为执行了3次查询:
Captured queries were: 1. SELECT "store_record"."id", "store_record"."key", "store_record"."database_id", "store_record"."user_id", "store_record"."organization_id", "store_record"."data", "store_record"."created_at", "store_record"."updated_at" FROM "store_record" WHERE "store_record"."database_id" = '7d86d143-9e4e-4420-801c-3d3c5e6875fb'::uuid LIMIT 21 2. SELECT "store_record"."id", "store_record"."key", "store_record"."database_id", "store_record"."user_id", "store_record"."organization_id", "store_record"."data", "store_record"."created_at", "store_record"."updated_at" FROM "store_record" WHERE "store_record"."database_id" = '7d86d143-9e4e-4420-801c-3d3c5e6875fb'::uuid LIMIT 21 3. SELECT "store_record"."id", "store_record"."key", "store_record"."database_id", "store_record"."user_id", "store_record"."organization_id", "store_record"."data", "store_record"."created_at", "store_record"."updated_at" FROM "store_record" WHERE "store_record"."database_id" = '7d86d143-9e4e-4420-801c-3d3c5e6875fb'::uuid LIMIT 21
问题原因与解决方法
核心原因:
.all()生成新QuerySet实例
Django的QuerySet是惰性求值的,但.all()方法每次调用都会返回一个全新的QuerySet对象,而非复用之前的查询结果。三次database.records.all()对应三个独立的QuerySet,每次打印时都会触发各自的数据库查询,因此产生了3次SQL请求。正确的缓存方式:复用同一个QuerySet实例
要实现缓存效果,需要将QuerySet绑定到变量上,复用同一个实例:def test_cache(self): with self.assertNumQueries(1): records = database.records.all() print(records) print(records) print(records)第一次打印时,QuerySet被求值并执行数据库查询,结果会被缓存到该实例中;后续再次访问
records时,会直接使用缓存的结果,不会触发新的查询。额外说明:QuerySet的缓存规则
Django的QuerySet缓存仅针对同一个实例生效。一旦实例被求值(比如迭代、打印、转换为列表),结果就会被缓存。但调用.all()、.filter()、.exclude()等查询方法时,都会生成新的QuerySet实例,这些新实例之间不会共享缓存。
内容的提问来源于stack exchange,提问作者Johnny Metz
相关产品推荐
相关产品推荐

