添加自定义Django模型验证

时间:2011-09-09 19:10:20

标签: python django django-models

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

e.g。我不想在表单中重新实现此逻辑ModelForm 管理表单模型被覆盖save()

据我所知,Django并不容易全球实施这些类型的标准。

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

4 个答案:

答案 0 :(得分:43)

我发现有用的基本模式是将所有自定义验证放在clean()中,然后只需调用full_clean()(调用clean()和其他一些方法){ {1}},例如:

save()

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

答案 1 :(得分:20)

我会覆盖模型上的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进行了很好的讨论。

答案 2 :(得分:10)

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

只需在模型中定义clean()方法,如下所示:(来自docs链接的示例)

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

答案 3 :(得分:1)

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

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