Django多对多through中间模型prefetch预取解决N+1查询问题
问题原因
你两次预取报错的核心原因是自定义了外键反向关联名称,覆盖了Django默认生成的{模型名小写}_set规则:
你在BlockLink的source外键上配置了related_name='source_block',所以从Block反向查询它作为源的所有中间表关联时,要使用source_block作为关联名,而不是默认的blocklink_set,也不是你第二次写的blocklink。
正确实现方案
场景1:不需要过滤中间表数据
直接使用简化预取写法,同时预取中间表关联的目标Block对象:
# 预取所有作为源的中间表关联,以及关联的目标Block blocks = Block.objects.prefetch_related('source_block', 'source_block__destination').all()
循环访问时直接用block.source_block.all()即可,不会触发额外查询。
场景2:需要过滤中间表数据(比如只取有效关联)
用Prefetch对象自定义查询条件,还可以指定自定义属性存储结果,避免覆盖默认关联:
from django.db.models import Prefetch blocks = Block.objects.prefetch_related( Prefetch( 'source_block', # 过滤有效关联,同时预取关联的目标Block减少后续查询 queryset=BlockLink.objects.filter(is_valid=True).select_related('destination'), to_attr='valid_outgoing_links' ) ).all()
访问方式:
for block in blocks: # 直接访问预取结果,无额外SQL查询 for link in block.valid_outgoing_links: print(f"源块:{block},目标块:{link.destination},是否有效:{link.is_valid}")
如果需要预取当前Block作为目标的关联,同理使用destination_block作为关联名配置预取即可。
内容的提问来源于stack exchange,提问作者ddjjaannggoo
相关产品推荐
相关产品推荐

