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




