Django 查询调用后会保存结果吗?
我在尝试弄清楚一个简单的缓存技巧是否真的有用。我知道Django的查询集是懒加载的,这样可以提高效率,但我在想,当数据被调用后,它们是否会保存查询的结果。
比如说,我有两个模型:
class Klass1(models.Model):
k2 = models.ForeignKey('Klass2')
class Klass2(models.Model):
# Model Code ...
@property
def klasses(self):
self.klasses = Klass1.objects.filter(k2=self)
return self.klasses
如果我在某个地方调用了 klass_2_instance.klasses[:]
,那么就会访问数据库并返回一个查询结果。我想知道,如果我再调用 klass_2_instance.klasses
,数据库会不会再次被访问,还是说Django会保存第一次调用的结果?
3 个回答
1
这个查询在Django中没有被缓存。
对于正向的外键关系,也就是当你有一个Klass
对象klass
,然后用klass.k2
去查找,第一次查找后是会被缓存的。但是你这里做的反向查找,通常写作klass2.klass_set.all()
,是不会被缓存的。
你可以很简单地进行缓存:
@property
def klasses(self):
if not hasattr(self, '_klasses'):
self._klasses = self.klass_set.all()
return self._klasses
(注意,你现有的代码是不能工作的,因为你把方法klasses
覆盖成了一个属性。)
1
如果你想要对查询结果进行自动缓存,可以试试 johnny-cache。
1
Django不会自动为你缓存数据。
与其使用Klass1.objects.filter(k2=self)这样的方式,不如直接用self.klass1_set.all()。因为在一对多的关系中,Django总是会在多的一方创建一个集合。
我觉得这种缓存比较复杂,因为它需要记住所有使用过的过滤条件、排除条件和排序方式。虽然可以用设计良好的哈希表来实现,但至少应该有一个参数可以关闭缓存。
如果你想要使用缓存,可以这样做:
class Klass2(models.Model): def __init__(self, *args, **kwargs): self._klass1_cache = None super(Klass2, self).__init__(*args, **kwargs) def klasses(self): if self._klass1_cache is None: # Here you can't remove list(..) because it is forcing query execution exactly once. self._klass1_cache = list(self.klass1_set.all()) return self._klass1_cache
这在你需要多次遍历所有相关对象时非常有用。对我来说,这种情况经常出现在模板中,当我需要多次循环时。