Django:缓存聚合查询?

时间:2010-08-12 22:14:37

标签: django caching relational-database

我有一个Invoice模型,我想让total保持最新状态。所以我添加了这样的属性

@property
def item_total(self):
    if not hasattr(self, 'item_total_cache'):
        self.item_total_cache = self.items.aggregate(t=Sum('amount'))['t']
    return self.item_total_cache

因为在一个页面请求期间可能会多次调用item_total,并且我不想每次都访问数据库。这是一个很好的方法吗?我的Invoice对象是否会存在于一个页面请求之外,因此item_total实际上可能会过时吗?

理想情况下,我想在请求发票的同时计算item_total ...我知道我可以手动执行此操作,但Django管理站点,例如,不知道它是需要总和。有没有我可以“注入”这些额外的信息,以便所有Invoice.objects.all/filter/get()请求同时运行聚合?

2 个答案:

答案 0 :(得分:4)

我可以提出另一种解决方案吗? 为什么不存储计数并通过使用信号使其自动递增/递减?

这取决于你所拥有的读/写量,但缓存这样的东西可能是更有效的解决方案。而且可能还有点容易。

无论哪种方式,您都正确缓存计数。除非您以其他方式缓存,否则Invoice对象不应该过时。此缓存绑定到实例,并且通过新请求,您可以假设您获得了新实例。除非你从memcached那里得到你的物品。

至于在需要时注入缓存。如果您使用get()filter(),该怎么办?在这种情况下,您的计数会更低,并且您的缓存不正确。尽管如此,all()应该是相当可行的。您只需覆盖默认管理器即可在获取时自动添加此数据。但这意味着,对于您请求的每个Item,您的Invoice也会被提取。

答案 1 :(得分:3)

这是一个很好的解决方案,我之前使用和推荐的解决方案。只要您不在全局范围内保留对它的任何引用,您的Invoice对象就不会超出单个页面请求 - 如果它只是在视图中创建并传递给模板,那就没问题。

有几种方法可以让管理站点自动生成聚合。一种是定义自定义管理器并覆盖get_query_set以返回self.annotate(Sum('amount')) - 这将确保始终为Invoice模型上的每个查询创建聚合,这可能不是您想要的。

另一种方法是在管理器上定义一个单独的方法 - 例如with_aggregate - 返回该方法,然后覆盖ModelAdmin的{​​{1}}方法以返回结果那种方法。这使您可以更好地控制是否在自己的视图中使用聚合。