Django数据库一对多关系报错:'ManyToOneRel'对象不可迭代
哥们,折腾3天确实够闹心的,我来帮你捋捋问题出在哪——核心是你用错了Django定义模型关系的方式,再加上视图里的对象取值逻辑有问题,才导致了这个报错。
第一步:修正模型定义
Django里实现一对多关系,只需要在「多」的一方(也就是Alternativas)定义ForeignKey就够了,Django会自动为「一」的一方(Questoes)生成反向关联属性,不用手动加ManyToOneRel(那是Django内部用的底层类,不是给开发者直接用的)。
把你的models.py改成这样:
from django.db import models from django.db.models.deletion import CASCADE class Questoes(models.Model): id = models.IntegerField(max_length=10, primary_key=True) enunciado = models.CharField(max_length=255, null=False) alternativa_correta = models.CharField(max_length=255, null=True) # 这里不需要手动添加关系字段,反向关联由下方ForeignKey自动生成 class Alternativas(models.Model): id = models.IntegerField(max_length=10, primary_key=True) # 加个related_name,让反向关联的名字更贴合你的业务场景 questao_id = models.ForeignKey('Questoes', on_delete=CASCADE, db_column="questao_id", related_name="respostas") descricao = models.CharField(max_length=255, null=True)
这里的related_name="respostas"是关键,之后你就可以用questao.respostas直接访问该题目对应的所有选项,比Django默认的alternativas_set更直观。
第二步:修正视图代码
你之前把查询到的题目对象塞进了字典的'questoes'键里,但遍历的时候直接用questoes.resposta——首先字典根本没有这个属性,其次就算拿到了正确的题目对象,原来的字段名也不对。
修正后的views.py:
def dashboard(request): if request.method == 'POST': # 直接获取Questoes对象,不用绕字典(如果业务需要字典也可以,但要正确取值) questao = Questoes.objects.get(id=1) print('=======================================================================') print(questao) print('=======================================================================') # 反向关联的属性是QuerySet,需要用.all()获取所有结果再遍历 for alternativa in questao.respostas.all(): print(alternativa.descricao) print('======================================================================')
要注意:反向关联的respostas是一个RelatedManager对象,不能直接遍历,必须调用.all()转换成可迭代的QuerySet才行。
为啥原来的代码会报错?
你手动添加的ManyToOneRel是Django内部用来管理关系的底层类,它不是一个可迭代的查询集合,所以当你尝试遍历它时,就会抛出'ManyToOneRel' object is not iterable的错误。正确的做法是依靠ForeignKey自动生成的反向关联来实现一对多的查询逻辑。
内容的提问来源于stack exchange,提问作者Fábio S. Monreal

