You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django缓存查询后取对象属性是否触库?缓存框架使用咨询

嘿,我来帮你把这两个Django缓存相关的问题讲清楚,都是很容易踩的坑~

问题1:从缓存的查询结果中请求对象属性时,Django会访问数据库吗?

答案是不一定,核心取决于你缓存的内容:

  • 如果你缓存的是未执行的QuerySet(比如直接缓存Model.objects.all()):这时候缓存的只是查询的“蓝图”,并没有实际从数据库拉取数据。当你后续访问对象属性时,QuerySet才会被触发执行,这时候就会访问数据库。
  • 如果你缓存的是已经执行过的查询结果(比如转成列表的list(Model.objects.all())、values()/values_list()返回的数据,或者序列化后的对象):数据已经存在内存里了,访问属性不会触发数据库查询。
问题2:你的代码示例中,访问list[0].title会触发数据库查询吗?

会的!问题出在你的缓存逻辑上。

先看你的代码:

@classmethod
def get_all_books(cls):
    query = cache.get_or_set('all_books', cls.objects.all())
    return query

这里你缓存的是cls.objects.all()这个QuerySet对象,而不是实际的书籍数据。Django的QuerySet是惰性执行的——只有当你真正需要用到数据的时候(比如转成列表、取索引、遍历它),它才会去数据库执行查询。

所以你在shell里的执行流程是:

  1. list = Book.get_all_books():从缓存拿到QuerySet,但此时还没查数据库。
  2. list[0].title:这时候触发了QuerySet的执行,它会去数据库拉取所有书籍数据,再返回第一个对象的title。

更糟的是,哪怕你缓存了这个QuerySet,每次后续触发它执行时,都会重新去数据库查询——因为QuerySet本身只是查询模板,不会缓存数据。

怎么修正?

你需要缓存实际的查询结果,而不是QuerySet,有两种简单的实现方式:

方式1:手动执行QuerySet再缓存

@classmethod
def get_all_books(cls):
    books = cache.get('all_books')
    if not books:
        # 用list()触发QuerySet执行,拿到实际的对象列表
        books = list(cls.objects.all())
        cache.set('all_books', books)
    return books

方式2:用get_or_set的回调函数

get_or_set的第二个参数如果是可调用对象(比如lambda函数),会在缓存不存在时执行这个函数,把返回结果存入缓存,这样能确保缓存的是执行后的结果:

@classmethod
def get_all_books(cls):
    # 用lambda包裹查询,确保缓存的是执行后的列表
    books = cache.get_or_set('all_books', lambda: list(cls.objects.all()))
    return books

额外提醒:处理外键的N+1问题

如果你的Book模型有外键(比如示例里的author),哪怕你缓存了Book列表,默认访问book.author.username时还是会触发额外的数据库查询(因为author没有被预取)。建议查询时加上select_related(针对一对一/外键)或prefetch_related(针对多对多),再缓存结果:

books = list(cls.objects.all().select_related('author'))

内容的提问来源于stack exchange,提问作者Amin Hemati Nik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:57:03