如果模型表单排除了某些字段,当模型具有干净方法时如何处理模型表单的验证?
How to handle the validation of the model form when the model has a clean method if the model form excluded some fields?
我有这个型号:
class IeltsExam(Model):
student = OneToOneField(Student, on_delete=CASCADE)
has_taken_exam = BooleanField(default=False,)
listening = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
reading = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
exam_date = DateField(null=True, blank=True, )
non_empty_fields = \
{
'listening': 'please enter your listening score',
'reading': 'please enter your reading score',
'exam_date': 'please specify your exam date',
}
def clean(self):
errors = {}
if self.has_taken_exam:
for field_name, field_error in self.non_empty_fields.items():
if getattr(self, field_name) is None:
errors[field_name] = field_error
if errors:
raise ValidationError(errors)
并拥有此模型
class IeltsExamForm(ModelForm):
class Meta:
model = IeltsExam
fields = ('has_taken_exam', 'listening', 'reading', )
当我在模板中提交此表单时,出现以下错误:
ValueError at /
'ExamForm' has no field named 'exam_date'.
和
During handling of the above exception ({'listening': ['please enter your listening score'], 'reading': ['please enter your reading score'], 'exam_date': ['please specify your exam date']}), another exception occurred:
错误发生在我验证表单的地方。
我的数据库逻辑是这样的,我需要有一个 exam_date 字段,如果选中 has_taken_exam 则应该强制填写。但是,在 ExamForm 中,出于业务原因,我不需要 exam_date。
我如何告诉 ExamForm 对 exam_date 视而不见,因为我没有保存模型实例?
对模型的 save()
执行验证
考虑以下模型:
class Exam(Model):
student = OneToOneField(Student, on_delete=CASCADE)
has_taken_exam = BooleanField(default=False)
score = FloatField(choices=SCORE_CHOICES, null=True, blank=True)
exam_date = DateField(null=True, blank=True)
def save(self, *a, **kw):
if self.has_taken_exam and not self.exam_date:
raise ValidationError("Exam date must be set when has_taken_exam is True")
return super().save()
ModelForm
初始化后,它有一个 instance
属性,这是调用 clean()
的模型实例。因此,如果您从实例的 non_empty_fields
字典中删除 exam_date
,它将不会在 clean
:
中使用它
class IeltsExamForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance.non_empty_fields.pop('exam_date')
您可以为 self._meta.exclude
中的每个字段执行此操作。
但是,这样做时,属性 non_empty_fields
不应是 class 属性,而是实例 属性。修改实例的 non_empty_fields
实际上会修改 class 属性(它是一个字典,所以它是可变的),这将产生意想不到的副作用(一旦删除,它会在您创建的任何后续实例中被删除)。更改模型以在 init 方法中设置属性:
class IeltsExam(Model):
# ...
# remove the class attribute non_empty_fields
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.non_empty_fields = { ... }
一般来说,如果您确实要保存模型,我建议您只使用 ModelForm
,在这种情况下,class 属性是更简洁的方法。如果您的表单不打算保存实际模型,而不是执行所有这些操作,则不应使用 ModelForm
而应使用 Form
并在表单本身中定义所有字段和清理。
我有这个型号:
class IeltsExam(Model):
student = OneToOneField(Student, on_delete=CASCADE)
has_taken_exam = BooleanField(default=False,)
listening = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
reading = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
exam_date = DateField(null=True, blank=True, )
non_empty_fields = \
{
'listening': 'please enter your listening score',
'reading': 'please enter your reading score',
'exam_date': 'please specify your exam date',
}
def clean(self):
errors = {}
if self.has_taken_exam:
for field_name, field_error in self.non_empty_fields.items():
if getattr(self, field_name) is None:
errors[field_name] = field_error
if errors:
raise ValidationError(errors)
并拥有此模型
class IeltsExamForm(ModelForm):
class Meta:
model = IeltsExam
fields = ('has_taken_exam', 'listening', 'reading', )
当我在模板中提交此表单时,出现以下错误:
ValueError at /
'ExamForm' has no field named 'exam_date'.
和
During handling of the above exception ({'listening': ['please enter your listening score'], 'reading': ['please enter your reading score'], 'exam_date': ['please specify your exam date']}), another exception occurred:
错误发生在我验证表单的地方。 我的数据库逻辑是这样的,我需要有一个 exam_date 字段,如果选中 has_taken_exam 则应该强制填写。但是,在 ExamForm 中,出于业务原因,我不需要 exam_date。 我如何告诉 ExamForm 对 exam_date 视而不见,因为我没有保存模型实例?
对模型的 save()
考虑以下模型:
class Exam(Model):
student = OneToOneField(Student, on_delete=CASCADE)
has_taken_exam = BooleanField(default=False)
score = FloatField(choices=SCORE_CHOICES, null=True, blank=True)
exam_date = DateField(null=True, blank=True)
def save(self, *a, **kw):
if self.has_taken_exam and not self.exam_date:
raise ValidationError("Exam date must be set when has_taken_exam is True")
return super().save()
ModelForm
初始化后,它有一个 instance
属性,这是调用 clean()
的模型实例。因此,如果您从实例的 non_empty_fields
字典中删除 exam_date
,它将不会在 clean
:
class IeltsExamForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance.non_empty_fields.pop('exam_date')
您可以为 self._meta.exclude
中的每个字段执行此操作。
但是,这样做时,属性 non_empty_fields
不应是 class 属性,而是实例 属性。修改实例的 non_empty_fields
实际上会修改 class 属性(它是一个字典,所以它是可变的),这将产生意想不到的副作用(一旦删除,它会在您创建的任何后续实例中被删除)。更改模型以在 init 方法中设置属性:
class IeltsExam(Model):
# ...
# remove the class attribute non_empty_fields
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.non_empty_fields = { ... }
一般来说,如果您确实要保存模型,我建议您只使用 ModelForm
,在这种情况下,class 属性是更简洁的方法。如果您的表单不打算保存实际模型,而不是执行所有这些操作,则不应使用 ModelForm
而应使用 Form
并在表单本身中定义所有字段和清理。