据我了解,当一个人创建一个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)
答案 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')
此代码将针对执行它的模块内定义的任何模型运行,但您可以将其调整为更严格的范围或根据需要成为一组模块/应用程序。