Django 查询调用后会保存结果吗?

2 投票
3 回答
677 浏览
提问于 2025-04-16 07:57

我在尝试弄清楚一个简单的缓存技巧是否真的有用。我知道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

这在你需要多次遍历所有相关对象时非常有用。对我来说,这种情况经常出现在模板中,当我需要多次循环时。

撰写回答