【问题标题】:Django model fields validationDjango 模型字段验证
【发布时间】:2010-12-10 03:13:16
【问题描述】:

模型字段的验证在django中应该去哪里?

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

可能的用例:

  • 绝对有必要确保不会将空字符串写入数据库(blank=False 关键字参数在此处不起作用,仅用于表单验证)
  • 当有必要确保时,“选择”关键字参数在数据库级别得到尊重,而不仅仅是在管理界面(模拟枚举数据类型)

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)

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

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    Django 从 1.2 版开始就有一个model validation 系统。

    在 cmets sebpiq 中说“好的,现在有一个放置模型验证的地方......除了它仅在使用 ModelForm 时运行!所以问题仍然存在,当有必要确保验证在db级,怎么办?full_clean在哪里调用?”

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

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

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

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

    【讨论】:

    • 太棒了,太棒了!我已经浏览了源代码(顺便说一句,有什么方法可以访问文档,因为它可以通过主干完成?)它似乎正是我需要的东西。我的意思是,我完全赞成自己动手,但 django 在提供统一的做事方式方面非常出色(好吧,无论如何,IMO)。
    • 检查那个分支,确保你已经安装了 docutils 和 Sphinx,然后进入 docs/ 目录并运行“make html”。这应该像在 Django 网站上一样以 HTML 形式构建文档,并且您可以在本地访问它们。
    • 好的,我从阅读源代码回来了(特别是 models/fields/__init__.py、models/base.py 和 core/validators.py),因为到目前为止的文档没有提到模型验证。然而,应该注意的是,它的工作原理与表单验证几乎相同(至少,一般逻辑或多或少相同)。无论如何,这是我一直在寻找的东西。我只是希望如果我只是从主干切换到这个分支,我的应用程序不会严重崩溃。
    • 我当然很想帮助 django。而且,由于缺乏文档,我不得不求助于代码潜水,在 django 的情况下,这根本不是一次不愉快的经历,它让我对事情的工作方式有了深刻的理解(尽管花了一些时间时间)。顺便说一句,没有什么东西真的坏了,验证部分确实有效。
    • 它仍然没有回答问题......好的,现在有一个地方可以放置模型验证......除了它只在使用 ModelForm 时运行!所以问题仍然是“当有必要确保[任何验证]在数据库级别得到尊重时”你应该怎么做?在哪里调用“full_clean”?
    【解决方案2】:

    我想你想要这个 ->

    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/

    【讨论】:

    • 不,不是。模型验证目前在 django 中是稳定的,所以原始问题的重点是没有实际意义
    • 我不这么认为,因为当你保存时验证器不会运行,所以它会让你添加不验证的东西。来自docs.djangoproject.com/en/dev/releases/1.2/#model-validation“仅调用模型实例的 save() 方法不会对实例的数据进行任何验证。”虽然也许我误解了 OP 想要什么,但是 肯定想要一种让 save() 验证事情的方法:)
    【解决方案3】:

    这个问题的根本问题是验证应该发生在模型上。这已经在 django 中讨论了很长一段时间(在开发邮件列表上搜索表单模型感知验证)。它会导致重复或在访问数据库之前逃避验证。

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

    【讨论】:

    • 我已经在谷歌缓存中阅读了它的内容。确实有道理,是的。如果我不打算使用表单(不需要表单来输入数据,对吗?),这不是很有帮助,但它肯定是一种避免重复自己的方法。
    【解决方案4】:

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

    【讨论】:

    • 这实际上是个好主意。但是,我特别提到了 .to_python(),因为它会在字段初始化后被触发(如果您指定 metaclass = models.SubfieldBase),因此验证会提前进行,这意味着您甚至不能如果您为字段传递错误值,请初始化模型。也许,你的方式是正确的方式,虽然。至少这对我来说有点道理。
    猜你喜欢
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 2020-02-08
    • 1970-01-01
    • 2020-11-29
    • 2014-05-12
    • 1970-01-01
    相关资源
    最近更新 更多