【问题标题】:Use ManyToMany field in add object raise needs to have a value for field "id" before this many-to-many relationship can be used在添加对象中使用 ManyToMany 字段 raise 需要为字段“id”设置一个值,然后才能使用这种多对多关系
【发布时间】:2019-08-08 12:01:43
【问题描述】:

我有两个模型:ExamQuestion。每个问题都有在每次考试中自动计算的分数。

这是我的文件:

#Models.py
class Exam(models.Model):
    questions = models.ManyToManyField(Question)
    title = models.CharField()
class Question(models.Model):
    title = models.CharField()
    answer = models.TextField()
    points = models.PositiveSmallIntegerField()

#Forms.py
class ExamForm(ModelForm):
    class Meta:
        model = Exam
        fields = '__all__'

#Views.py
if form.is_valid():
    new_exam = form.save(commit=False)
    # Some modify goes here. 
    new_exam.save()
    form.save_m2m()
    return redirect('view_exam')

我确实自定义了Exam模型的save()方法:

 def save(self, *args, **kwargs):
        self.point = 0
        for question in self.questions:
                self.point += question.point
        super(Exam, self).save(*args, **kwargs)

但我得到了这个错误:

"<Exam: NewObject>" needs to have a value for field "id" before this many-to-many relationship can be used.

我怎样才能做到这一点而不引发任何错误?

我的目标:对于每一个新创建的考试,计算本次考试的题目分数,并将它们放入Exam模型的points字段中。

【问题讨论】:

  • 在保存对象之前您无法访问self.questions。首先调用super().save() 方法,然后访问问题。但这毫无意义,因为: 1. Exam 没有字段 point 所以设置 self.point 没有意义,因为它没有保存,因此一旦退出 form_valid() 方法就会丢失。 2. questions 还没有被保存(因为你还没有调用form.save_m2m())所以对于新对象,什么都不会发生。
  • 告诉我们您想用自定义方法做什么,然后我们可能会帮助提出替代方案。现在你应该删除你的自定义保存方法,因为它没有任何作用。然后一切都会正常。
  • 最后,我必须做什么?请写下你的答案。 @dirkgroten
  • 好的,我在我的问题中写下我的目的。 @dirkgroten
  • 我写了我要找的东西。 @dirkgroten

标签: python django django-models many-to-many django-2.2


【解决方案1】:

将可以从数据库中的其他字段/表计算出来的东西保存到模型中绝不是一个好主意,尤其是当这取决于其他模型时。例如,当您在某个阶段创建新的Question 时,太容易忘记更新值。你只会在你的数据库中得到不一致。

删除您的自定义save() 方法,因为它没有任何作用。

如果您想知道总点数,请在 Exam 上添加一个自定义 getter,它会即时计算:

#At the first of models.py -> from django.db.models import Sum 
@property
def points(self):
    if self.pk:
       return self.questions.all().aggregate(Sum('points'))['points__sum']
    else:
       return 0

或者用你的总结:

@property
def points(self):
    if self.pk:
        point = 0
        questions = self.questions.all()
        for question in questions :
            point += question.point
        return point
    else:
        return 0

使用此属性,您可以在代码中的任何位置执行exam.points,它将是最新的。

【讨论】:

  • 所以你建议不要这样做,实际上是在Views.py 级别进行? @dirkgroten
  • 不,抱歉,改变了我的论点的顺序。不要添加字段点并且不要保存它就是我的意思。通过将我上面给出的方法(作为属性)添加到您的模型考试中,即时计算它。如果您真的想保存它,您可以在视图级别执行此操作,是的,但我建议您不要这样做,因为您需要记住在视图级别为所有修改问题和考试的视图执行此操作。
  • Sum 无效!我怎样才能在我看来得到这个属性? @dirkgroten。
  • 当我使用 exam.points 时出现此错误:+: 'int' 和 'str' @dirkgroten 的操作数类型不受支持
  • 你需要导入 Sum
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-31
  • 2017-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多