如何在 django 中使用模型清理方法并遍历字段

Posted

技术标签:

【中文标题】如何在 django 中使用模型清理方法并遍历字段【英文标题】:How to use model clean method in django and iterate over fields 【发布时间】:2020-01-30 19:54:13 【问题描述】:

我问了一个问题,我读了一点,现在可以更好地表达我的需要: How to do model level custom field validation in django?

我有这个模型:

class StudentIelts(Model):

    SCORE_CHOICES = [(i/2, i/2) for i in range(0, 19)]

    student = OneToOneField(Student, on_delete=CASCADE)
    has_ielts = BooleanField(default=False,)
    ielts_listening = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    ielts_reading = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    ielts_writing = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    ielts_speaking = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )

并有这个模型形式:

class StudentIeltsForm(ModelForm):

    class Meta:
        model = StudentIelts
        exclude = ('student')

    def clean(self):
        cleaned_data = super().clean()
        has_ielts = cleaned_data.get("has_ielts")

        if has_ielts:
            msg = "Please enter your score."
            for field in self.fields:
                if not self.cleaned_data.get(str(field)):
                    self.add_error(str(field), msg)

        else:
            for field in self.fields:
                self.cleaned_data[str(field)] = None
                self.cleaned_data['has_ielts'] = False

        return cleaned_data

我在这里所做的是检查has_ielts 是否为True,然后应填写所有其他字段。如果has_ieltsTrue 甚至没有填写一个字段,我会收到错误消息。如果has_ieltsFalse,则应保存具有has_ielts=False 和所有其他字段Null 的对象。我现在想在模型级别上做:

class StudentIelts(Model):

    SCORE_CHOICES = [(i/2, i/2) for i in range(0, 19)]

    student = OneToOneField(Student, on_delete=CASCADE)
    has_ielts = BooleanField(default=False,)
    ielts_listening = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    ielts_reading = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    ielts_writing = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )
    ielts_speaking = FloatField(choices=SCORE_CHOICES, null=True, blank=True, )

    def clean(self):
    # I do not know what to write in here

并有这个模型形式:

class StudentIeltsForm(ModelForm):

    class Meta:
        model = StudentIelts
        exclude = ('student')

在我的模型的 clean 方法中,我想要一些具有这种逻辑的东西(这是伪代码):

def clean(self):
    msg = "Please enter your score."

    if self.has_ielts:
        my_dic = 
        for f in model_fields:
             if f is None:
                  my_dic.updatestr(field_name): msg
        raise ValidationError(my_dic)

我该怎么做? 如何在模型级别获得与我的模型表单相同的结果?

【问题讨论】:

您需要明确地逐一检查 4 个字段(也在您的表单中),因为您只对以 ielts 开头的字段感兴趣。包含其他字段是不好的编码。 if not ielts_listening: raise ValidationError(...); .... 我希望有一个可重用的代码用于其他模型,如 GRE、GMAT、IBT 等,在我当前的模型表单中,我正在遍历所有字段并更改其中一个。在拥有可重用代码的同时,应该有一种简洁的方法。这没有帮助吗? :for field in self._meta.fields: 但其中包括“id”、“student”等字段以及您稍后添加的其他字段。您可以将需要非空的字段列表一起定义为类属性并循环浏览该列表。不这样做会给使用您的模型的其他开发人员带来不可预测的结果。 @Daniel 我遇到了与此问题相关的问题,可在此处找到:***.com/questions/58254333/… 【参考方案1】:

您需要显式声明应为非空的字段,否则您将循环浏览与您的 clean 方法无关的字段,例如 idstudent。有人可能想稍后添加一个非强制性字段,并想知道为什么会引发验证错误。

class StudentIelts(Model):

    # fields

    non_empty_fields = ['ielts_reading', ...]

    def clean(self):
        errors = 
        if self.has_ielts:
            for field_name in self.non_empty_fields:
                if not getattr(self, field_name):
                    errors[field_name] = msg
        if errors:
            raise ValidationError(errors)

【讨论】:

这是 python 实际访问对象属性(和方法)的方法:getattr(self, "ielts_reading")self.ielts_reading 相同。请参阅this question 了解更多信息。 例如,Django 使用它来清理您定义 clean_FOO 方法的各个字段:getattr(self, f"clean_field_name")() 将实际获取该方法并调用它(由于 () 我不明白你的第一个问题。但是,是的,您在模型中进行的任何验证都不需要在模型表单中进行。通常,首先要开始对模型进行验证。如果此时的业务逻辑需要您执行额外的验证,或者如果您需要使用模型中不可用的变量进行验证,例如当前用户(模型验证中没有请求),您可以在模型表单中添加验证。 在抽象类中定义non_empty_fields = [],并在每个子类中覆盖它。但是这样你就不必重复 clean() 方法了。 然后检查if getattr(self, field_name) is Noneif getattr(self, field_name) in [None, False, ''] 而不是检查真实性。

以上是关于如何在 django 中使用模型清理方法并遍历字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Django 模型中的函数遍历 SQL 数据库?

遍历模型字段 - Django

显示对象表 django

Django ModelForm 浮点字段不调用 Clean_Field 方法

Django Modelform - 它没有验证,为什么?

Django Modelform - 它没有验证,为啥?