Django模型字段验证

时间:2009-10-26 13:27:15

标签: python django django-models

模型字段的验证应该放在哪里?

我至少可以命名两种可能的选择:在模型的重载.save()方法中或在models.Field子类的.to_python()方法中(显然,为了工作,你必须编写自定义字段)。

可能的用例:

  • 当绝对需要确保时,空字符串不会写入数据库(空白= False关键字参数在此处不起作用,仅用于表单验证)
  • 当需要确保时,“choices”关键字参数在db级别上得到尊重,而不仅仅在管理界面中(仿真枚举数据类型)

models.Field基类定义和派生类中还有一个类级别属性empty_strings_allowed,它很高兴地覆盖它,但它似乎不会对数据库级别产生任何影响,这意味着我仍然可以构造一个带有空字符串字段的模型并将其保存到数据库中。我想避免(是的,这是必要的)。

可能的实施是

在字段级别:

class CustomField(models.CharField):
    __metaclass__ = models.SubfieldBase
    def to_python(self, value):
        if not value:
            raise IntegrityError(_('Empty string not allowed'))
        return models.CharField.to_python(self, value)

在模型级别:

class MyModel(models.Model)
    FIELD1_CHOICES = ['foo', 'bar', 'baz']
    field1 = models.CharField(max_length=255, 
               choices=[(item,item) for item in FIELD1_CHOICES])

    def save(self, force_insert=False, force_update=False):
        if self.field1 not in MyModel.FIELD1_CHOICES:
            raise IntegrityError(_('Invalid value of field1'))
        # this can, of course, be made more generic
        models.Model.save(self, force_insert, force_update)

也许,我错过了一些东西,这可以更容易(也更清洁)?

4 个答案:

答案 0 :(得分:61)

自版本1.2以来,Django已经建立了model validation系统。

在评论中,sebpiq说“好的,现在有一个地方可以进行模型验证......除了它只在使用ModelForm时运行!所以问题仍然存在,当有必要确保验证在db-level,你应该怎么做?在哪里调用full_clean?“

通过Python级别验证无法确保在数据库级别上遵守验证。最接近的可能是在覆盖的full_clean方法中调用save。默认情况下不会这样做,因为这意味着调用save方法的每个人现在都已准备好捕获并处理ValidationError

但即使你这样做,有人仍然可以使用queryset.update()批量更新模型实例,这将绕过此验证。 Django无法实现一个合理有效的queryset.update(),它仍然可以对每个更新的对象执行Python级别的验证。

真正保证数据库级完整性的唯一方法是通过db级约束;您通过ORM进行的任何验证都要求应用程序代码的编写者了解何时强制执行验证(并处理验证失败)。

这就是默认情况下模型验证仅在ModelForm中强制执行的原因 - 因为在ModelForm中已经有一种处理ValidationError的明显方法。

答案 1 :(得分:6)

我想你想要这个 - >

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')

(从http://djangosnippets.org/snippets/2319/复制)

答案 2 :(得分:3)

这个问题的根本问题是验证应该在模型上进行。这已经在django中讨论了很长时间(在dev邮件列表上搜索表单模型识别验证)。它会导致重复或在达到db之前逃避验证的事情。

虽然没有达到主干,但Malcolm的"poor man's model validation solution"可能是避免重复自己的最干净的解决方案。

答案 3 :(得分:1)

如果我“清楚地”理解你 - 你必须覆盖函数get_db_prep_save而不是to_python