如果模型表单排除了某些字段,那么当模型具有干净方法时,如何处理模型表单的验证? [英] 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', )
当我在模板中提交此表单时,出现以下错误:
when I submit this form in template, I get the below error:
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中,我不需要test_date. 由于我没有保存modelform实例,我该如何告诉ExamForm对EXAM_DATE视而不见?
The error happens in my view where I am validating the form. My database logic is such that I need to have an exam_date field and it should be mandatory to fill if has_taken_exam is checked. However, in ExamForm, for business reasons, I do not need the exam_date. How can I tell ExamForm to turn a blind eye to the exam_date, as I am not saving the modelform instance?
推荐答案
ModelForm
初始化后,它具有instance
属性,该属性是将在其上调用clean()
的模型实例.因此,如果您从实例的non_empty_fields
字典中删除exam_date
,它将不会在clean
中使用它:
After the ModelForm
is initialised, it has an instance
attribute which is the model instance on which clean()
will be called. So if you remove exam_date
from the instance's non_empty_fields
dictionary, it won't use it in clean
:
class IeltsExamForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance.non_empty_fields.pop('exam_date')
您可以对self._meta.exclude
中的每个字段执行此操作.
And you could do that for each field in self._meta.exclude
.
但是,在执行此操作时,属性non_empty_fields
不应是类属性,而应该是实例属性.修改实例的non_empty_fields
实际上是修改了class属性(它是一个字典,因此是可变的),这会产生意想不到的副作用(一旦删除,则对于您创建的任何后续实例都会删除它).更改模型以在init方法中设置属性:
However, when doing that, the attribute non_empty_fields
should not be a class attribute but an instance property. Modifying the instance's non_empty_fields
actually modifies the class attribute (it's a dictionary so it's mutable), which will have unintended side-effects (once removed, it's removed for any subsequent instance you create). Change your model to set the attribute in the init method:
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
并在表单本身中定义所有字段和清理对象.
In general, I would advise you to only use a ModelForm
if you're actually going to save the model, in which case a class attribute is the cleaner approach. Instead of doing all this, if your form isn't going to save the actual model, you should not use a ModelForm
but a Form
and define all fields and cleaning in the form itself.
这篇关于如果模型表单排除了某些字段,那么当模型具有干净方法时,如何处理模型表单的验证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!