You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Django中prefetch_related后如何更新对象获取新增关联数据?

问题描述

我定义了如下Django模型:

class Publisher(models.Model):
    name = models.CharField(max_length=30)
class Book(models.Model):
    title = models.CharField(max_length=100)
    publisher = models.ForeignKey(Publisher)

views.py中,为了展示出版社页面并同时显示其关联书籍,我用了预取关联的查询:

publisher = Publisher.objects.prefetch_related('book_set').filter(pk=id).first()

之后我会遍历关联书籍做操作:

for book in publisher.book_set.all():
    foo()

这段代码运行正常,但有个问题:如果在查询操作和遍历操作之间有新的书籍被添加到该出版社,由于数据已经被预取,publisher.book_set.all()无法获取到新增的书籍。请问有没有办法更新这个publisher对象来获取最新的数据?


解决方案

这里有几种可行的方法来解决这个问题:

  • 重新执行查询获取最新对象
    最简单直接的方式就是重新查询一次出版社对象,根据需求选择是否继续预取关联数据:

    # 重新获取带最新预取数据的publisher对象
    publisher = Publisher.objects.prefetch_related('book_set').filter(pk=publisher.pk).first()
    # 或者不需要预取的话,直接获取后让关联查询走数据库
    publisher = Publisher.objects.get(pk=publisher.pk)
    
  • 清除预取缓存,强制重新查询关联数据
    Django会把预取的关联数据缓存到对象的_prefetched_objects_cache属性中,删除这个属性里对应关联的缓存,就能让后续的book_set.all()重新查询数据库:

    # 清除book_set的预取缓存
    if hasattr(publisher, '_prefetched_objects_cache'):
        del publisher._prefetched_objects_cache['book_set']
    # 此时调用all()会重新查询数据库,拿到最新的书籍
    for book in publisher.book_set.all():
        foo()
    

    注意:这个方法操作的是Django内部的缓存属性,属于半私有API,后续Django版本更新可能会有变动,使用时需要留意版本兼容性。

  • 结合refresh_from_db()与缓存清除
    Django模型实例的refresh_from_db()方法可以刷新自身字段的最新数据,默认不会刷新预取的关联数据,搭配清除缓存就能同时拿到出版社自身和关联书籍的最新数据:

    # 刷新publisher自身的字段数据
    publisher.refresh_from_db()
    # 清除预取缓存,让关联查询重新走数据库
    if hasattr(publisher, '_prefetched_objects_cache'):
        del publisher._prefetched_objects_cache['book_set']
    

内容的提问来源于stack exchange,提问作者Dalvtor

火山引擎 最新活动