Django 1.11.11中prefetch_related关联查询未按预期生效问题
我之前也碰到过一模一样的坑,咱们一步步排查解决:
1. 先确认prefetch是否真的执行了
先开Django的查询日志看看,有没有触发预取的SQL语句。在项目的settings.py里加这段配置:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'level': 'DEBUG', 'handlers': ['console'], }, }, }
运行代码后看控制台输出,如果有针对genres和tags的IN查询(比如SELECT ... FROM app_genre WHERE id IN (...)),说明预取已经执行了,问题出在后续调用没用到缓存;如果没有,那就是预取根本没触发。
2. 检查关联模型的自定义Manager
如果Genre或Tag用了自定义Manager,而且这个Manager带默认过滤条件,会干扰预取结果。比如:
class GenreManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(is_active=True)
这种情况下,预取会拉所有关联的Genre,但你调用subsection.genres.all()时,会自动加上Manager的默认过滤,导致重新查数据库。解决办法是预取时明确指定用不带过滤的QuerySet:
from django.db.models import Prefetch subsection = Subsection.objects.filter(**q_objects).prefetch_related( Prefetch('genres', queryset=Genre.objects.all()), Prefetch('tags', queryset=Tag.objects.all()) ).first()
3. 试试替换first()的写法
Django 1.11里first()偶尔会有预取绑定的小问题,你可以换成切片取第一个试试:
subsections = Subsection.objects.filter(**q_objects).prefetch_related('genres', 'tags')[:1] if subsections: subsection = subsections[0]
再调用subsection.genres.all()看是否还触发新查询。
4. 手动检查缓存是否存在
Django会把预取的结果存在实例的_prefetched_objects_cache属性里,你可以直接打印看看:
print(subsection._prefetched_objects_cache)
如果里面有'genres'和'tags'这两个key,说明预取成功了,调用all()应该不会再查库;如果没有,就得回到前面找预取没触发的原因。
5. 排除事务或连接问题
如果你的代码是在事务里运行的,或者数据库连接有异常,也可能导致预取缓存没保存住。可以尝试在非事务环境下测试这段代码,看是否正常生效。
内容的提问来源于stack exchange,提问作者FVod
相关产品推荐
相关产品推荐

