【问题标题】:Adding Custom Django Model Validation添加自定义 Django 模型验证
【发布时间】:2011-11-14 01:07:39
【问题描述】:

我有一个带有开始和结束日期范围的 Django 模型。我想强制执行验证,以便没有两条记录具有重叠的日期范围。实现这一点的最简单方法是什么,这样我就不必重复自己编写这个逻辑了?

例如我不想在表单中重新实现这个逻辑 ModelForm 管理表单 模型被覆盖的@987654322 @。

据我所知,Django 并不容易在全球范围内强制执行这些类型的标准。

谷歌搜索并不是很有帮助,因为“模型验证”通常是指验证特定的模型字段,而不是整个模型内容或字段之间的关系。

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    我认为你应该使用这个: https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects

    只需像这样在模型中定义 clean() 方法:(来自文档链接的示例)

    def clean(self):
        from django.core.exceptions import ValidationError
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError('Draft entries may not have a publication date.')
        # Set the pub_date for published items if it hasn't been set already.
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.datetime.now()
    

    【讨论】:

    • 这很接近。我还必须覆盖模型的 save(),并从那里调用 clean()。
    • 但是为了什么? AdminSite (ModelForm) 自动调用 clean()。但是从 save() 方法调用 clean() 可能会在意外的时刻产生 ValidationError 并且不会像预期的那样咳嗽。
    • 并非所有东西都称为干净。无论保存在何处,都需要对其进行验证。损坏的网站页面比损坏的数据更可取。
    • @Cerin 从save 调用clean 似乎不是一个好主意。不要半途而废地遵循 Django 指南。如果您希望每个save() 都发生这种情况,那么请忘记clean 并将代码放入save 方法中。
    • @lajarre,复制错误检查代码是一个可怕的想法。您应该始终关注 DRY。
    【解决方案2】:

    我会覆盖模型上的validate_unique 方法。为确保在验证时忽略当前对象,您可以使用以下内容:

    from django.db.models import Model, DateTimeField
    from django.core.validators import NON_FIELD_ERRORS, ValidationError
    
    class MyModel(Model):
        start_date = DateTimeField()
        end_date = DateTimeField()
    
        def validate_unique(self, *args, **kwargs):
            super(MyModel, self).validate_unique(*args, **kwargs)
    
            qs = self.__class__._default_manager.filter(
                start_date__lt=self.end_date,
                end_date__gt=self.start_date
            )
    
            if not self._state.adding and self.pk is not None:
                qs = qs.exclude(pk=self.pk)
    
            if qs.exists():
                raise ValidationError({
                    NON_FIELD_ERRORS: ['overlapping date range',],
                })
    

    ModelForm 会通过full_clean() 自动为您调用,您也可以手动使用。

    PPR 对一个简单、正确的range overlap condition 进行了很好的讨论。

    【讨论】:

    • 在 django 1.3.1 上,我遇到了按照您描述的方式提出 ValidationError 的问题。在引发异常时,我可以通过传递 {field: (error_msg,)} 字典而不是字符串 error_msg 来修复它。
    • from django.core.exceptions import ValidationError, NON_FIELD_ERRORS raise ValidationError({NON_FIELD_ERRORS: ('overlapping date range',)})
    • 是否有兴趣扩展validate_unique 而不仅仅是定义clean?只是组织代码的问题?
    • validate_unique 暗示 [in it's method name] 您正在验证唯一约束。 Django 的“验证对象”文档指的是 clean 方法。在我看来,接受接受的答案并在那里进行验证似乎更合乎逻辑。
    【解决方案3】:

    我发现有用的基本模式是将所有自定义验证放在clean() 中,然后从save() 内部简单地调用full_clean()(它调用clean() 和其他一些方法),例如:

    class BaseModel(models.Model):
        
        def clean(self, *args, **kwargs):
            # add custom validation here
            super().clean(*args, **kwargs)
    
        def save(self, *args, **kwargs):
            self.full_clean()
            super().save(*args, **kwargs)
    

    默认情况下不会这样做,正如 here 所解释的那样,因为它会干扰某些功能,但这些对我的应用程序来说不是问题。

    【讨论】:

    • 在 Python 3 中,super(BaseModel, self) 可以简化为 super()
    【解决方案4】:

    我认为这可以帮助你, 我们可以在模型中创建多个像这样使用的验证器。

    from django.core.exceptions import ValidationError
    from django.utils.translation import gettext_lazy as _
    from django.db import models
    
    def validate_even(value):
        if value % 2 != 0:
            raise ValidationError(
                _('%(value)s is not an even number'),
                params={'value': value},
            )
    
    class MyModel(models.Model):
        even_field = models.IntegerField(validators=[validate_even])
    

    【讨论】:

    • 如何在这个验证器函数中访问self?
    猜你喜欢
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-09
    • 2015-03-08
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 2013-05-09
    相关资源
    最近更新 更多