Django自定义多对多关系下QuerySet过滤报错问题排查
Django多对多中间模型查询报错解决
问题背景
现有以下Django模型,Block和CustomCondition通过中间模型BlockCondition实现多对多关联,中间模型包含自定义字段choice:
from django.db import models class CustomCondition(models.Model): name = models.CharField("custom condition name") class Meta: db_table = "custom_conditions" def __str__(self) -> str: return self.name class BlockCondition(models.Model): block = models.ForeignKey( "Block", related_name="block_condition_as_block", on_delete=models.CASCADE, ) custom_condition = models.ForeignKey( CustomCondition, related_name="block_condition_as_custom_condition", on_delete=models.CASCADE, ) choice = models.CharField( "choice", choices=[ ("NO_CONDITION", "No condition"), ("ACTIVATED", "Activated"), ("NOT_ACTIVATED", "Not activated"), ], default="NO_CONDITION", ) class Meta: db_table = "block_conditions" def __str__(self) -> str: return self.custom_condition.name class Block(models.Model): name = models.CharField("name", null=False) block_conditions = models.ManyToManyField( CustomCondition, through="BlockCondition", blank=True, default=None, related_name="blocks_as_block_condition", ) class Meta: db_table = "blocks"
需求是遍历每个CustomCondition,筛选出对应条件处于ACTIVATED或NO_CONDITION状态的Block,执行以下代码时触发错误:
for c in CustomCondition.objects.all(): if c.name: filtered_blocks = Block.objects.filter( block_conditions__custom_condition__name=c.name ) filtered_blocks = filtered_blocks.filter( block_conditions__choice__in=["ACTIVATED", "NO_CONDITION"] ) print(filtered_blocks)
报错信息:
django.core.exceptions.FieldError: Unsupported lookup 'custom_condition' for ForeignKey or join on the field not permitted.
错误原因
block_conditions是Block关联CustomCondition的多对多字段,直接访问block_conditions__custom_condition属于逻辑错误——block_conditions本身就是CustomCondition实例,不需要再通过custom_condition字段反向关联。- 要查询中间模型
BlockCondition的choice字段,不能直接通过多对多字段block_conditions,必须通过中间模型的关联关系(即Block中block_condition_as_block这个related_name)来访问中间模型的字段。
修正后的代码
方式一:遍历每个CustomCondition查询(符合原需求逻辑)
for c in CustomCondition.objects.all(): if c.name: filtered_blocks = Block.objects.filter( # 通过中间模型的关联找到对应的CustomCondition block_condition_as_block__custom_condition=c, # 筛选中间模型的choice状态 block_condition_as_block__choice__in=["ACTIVATED", "NO_CONDITION"] ) print(filtered_blocks)
方式二:更高效的批量查询(避免循环查库)
如果不需要单独处理每个CustomCondition,可以一次性查询所有符合条件的Block与CustomCondition的对应关系:
from django.db.models import Prefetch # 预取符合条件的中间模型数据 prefetch = Prefetch( 'block_condition_as_block', queryset=BlockCondition.objects.filter( choice__in=["ACTIVATED", "NO_CONDITION"] ), to_attr='valid_block_conditions' ) # 查询所有带有有效条件的Block blocks = Block.objects.prefetch_related(prefetch).filter( valid_block_conditions__isnull=False ) # 遍历输出每个Block对应的有效CustomCondition for block in blocks: print(f"Block: {block.name}") for bc in block.valid_block_conditions: print(f" CustomCondition: {bc.custom_condition.name}, Choice: {bc.choice}")
内容的提问来源于stack exchange,提问作者bolino
相关产品推荐
相关产品推荐

