37

据我了解,当一个人创建 Django 应用程序时,数据在插入模型实例之前由表单验证,然后将其写入数据库。但是,如果我想在数据模型层创建一个额外的保护层,我所做的是否低于当前的“最佳实践”?我试图确保审稿人的姓名不能省略,也不能留空。我是否应该像我在这里所做的那样在“干净”方法中放置任何自定义验证,然后让“保存”调用“full_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

4 回答 4

45

full_clean首先,你不应该像你所做的那样覆盖。来自full_clean 上的 django 文档

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

所以该full_clean方法已经调用了clean,但是通过覆盖它,您已经阻止它调用其他两个方法。

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

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

最后,您的clean方法会起作用,但您实际上可以在模型字段本身中处理您的示例案例。定义你CharField

name = models.CharField(max_length=128)

blank选项将默认为 False。如果该字段为空,ValidationError则运行时会引发a full_clean。放入default=NoneyourCharField不会造成任何伤害,但是当您实际上不允许None作为值时,它会有点令人困惑。

于 2012-10-18T01:07:18.347 回答
7

在考虑了 Alasdair 的回答并进行了补充阅读之后,我现在的感觉是,Django 的模型并不是为了像我试图做的那样在仅模型的基础上进行验证。可以进行此类验证,但要付出一定的代价,并且需要以非预期的方式使用验证方法。

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

有人想吗?

于 2012-10-18T22:48:10.593 回答
6

在我的模型上捕获预保存信号确保 clean 将被自动调用。

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')
于 2016-04-26T02:04:24.530 回答
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')

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

于 2021-02-27T00:05:00.857 回答