这是验证Django模型字段的方法吗?

时间:2012-10-18 00:13:01

标签: python django django-models

据我了解,当一个人创建一个Django应用程序时,在将表单插入到模型实例中然后将其写入数据库之前,表单会对数据进行验证。但是如果我想在数据模型层创建一个额外的保护层,那么我在目前的“最佳实践”下面做了什么?我正在努力确保评论者的名字不能被省略,也不能留空。我应该在'clean'方法中进行任何自定义验证,就像我在这里做的那样,然后'保存'调用'full_clean'调用'clean'?如果不是,那么首选方法是什么?谢谢。

class Reviewer(models.Model):
    name = models.CharField(max_length=128, default=None)

    def clean(self, *args, **kwargs):
        if self.name == '':
            raise ValidationError('Reviewer name cannot be blank')
        super(Reviewer, self).clean(*args, **kwargs)

    def full_clean(self, *args, **kwargs):
        return self.clean(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.full_clean()
        super(Reviewer, self).save(*args, **kwargs)

4 个答案:

答案 0 :(得分:24)

首先,你不应该像你一样覆盖full_clean。来自django docs on full_clean

  

<强> Model.full_clean(exclude=None)
  此方法按此顺序调用Model.clean_fields()Model.clean()Model.validate_unique(),并引发ValidationError message_dict属性,其中包含来自所有三个阶段的错误。< / p>

因此full_clean方法已经调用clean,但是通过覆盖它,您已经阻止它调用其他两种方法。

其次,在full_clean方法中调用save是一种权衡。请注意,在验证模型表单时已调用full_clean,例如在Django管理员。因此,如果您在full_clean方法中调用save,则该方法将运行两次。

通常不期望save方法引发验证错误,有人可能会调用save而不会捕获结果错误。但是,我喜欢你调用full_clean而不是在save方法本身中进行检查 - 这种方法允许模型表单首先捕获问题。

最后,您的clean方法可行,但您实际上可以在模型字段中处理您的示例案例。将您的CharField定义为

name = models.CharField(max_length=128)

blank选项默认为False。如果该字段为空,则在您运行ValidationError时将引发full_clean。将default=None放在CharField中不会造成任何伤害,但如果您实际上不允许None作为值,则会有点混乱。

答案 1 :(得分:3)

在我的模型上捕获预保存信号,确保自动调用清洁。

from django.db.models.signals import pre_save

def validate_model(sender, **kwargs):
    if 'raw' in kwargs and not kwargs['raw']:
        kwargs['instance'].full_clean()

pre_save.connect(validate_model, dispatch_uid='validate_models')

答案 2 :(得分:2)

在考虑了Alasdair的回答并做了附加阅读之后,我现在的感觉是,Django的模型并没有被设计成只在模型上验证,正如我试图做的那样。这样的验证可以完成,但需要付出代价,并且需要以不适合的方式使用验证方法。

相反,我现在相信除了那些可以直接输入到模型字段声明中的约束(例如“unique = True”)之外的任何约束都应该作为Form或ModelForm验证的一部分来执行。如果想要防止通过任何其他方式(例如在Python解释器中工作时通过ORM)将无效数据输入项目的数据库,则验证应该在数据库本身内进行。因此,验证可以在三个层面上实现:1)首先,通过数据库中的DDL实现所有约束和触发器; 2)实现模型字段可用的任何约束(例如“unique = True”); 3)实现在Forms和ModelForms中镜像数据库级约束和触发器的所有其他约束和验证。使用此方法,可以将任何表单验证错误重新显示给用户。如果程序员通过ORM直接与数据库交互,他/她将直接看到数据库异常。

有人想到吗?

答案 3 :(得分:0)

感谢@Kevin Parker 的回答,很有帮助!

在您的应用中包含您定义的模型之外的模型是很常见的,因此这里有一个修改后的版本,您可以使用该版本将此行为仅适用于您自己的模型或特定的应用/模块。

from django.db.models.signals import pre_save
import inspect
import sys

MODELS = [obj for name, obj in
    inspect.getmembers(sys.modules[__name__], inspect.isclass)]

def validate_model(sender, instance, **kwargs):
    if 'raw' in kwargs and not kwargs['raw']:
        if type(instance) in MODELS:
            instance.full_clean()

pre_save.connect(validate_model, dispatch_uid='validate_models')

此代码将针对执行它的模块内定义的任何模型运行,但您可以将其调整为更严格的范围或根据需要成为一组模块/应用程序。