添加自定义Django模型验证
我有一个Django模型,它有一个开始日期和一个结束日期。我想要确保没有两个记录的日期范围是重叠的。有没有简单的方法来实现这个功能,这样我就不需要在不同地方重复写这个逻辑了?
比如说,我不想在表单、ModelForm
、管理员表单和模型的save()
方法中都重新实现这个逻辑。
据我所知,Django并没有很方便的方式来全局强制执行这些类型的规则。
在网上搜索也没什么帮助,因为“模型验证”通常指的是验证特定的模型字段,而不是整个模型的内容或字段之间的关系。
4 个回答
16
我觉得你应该使用这个链接: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()
29
我会在模型上重写 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 有一个很不错的讨论,讲了一个简单而正确的 日期范围重叠条件。
71
我发现一个很有用的基本模式,就是把所有自定义的验证逻辑放在 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)
默认情况下并不会这样做,具体原因可以在 这里 找到,因为这样会影响某些功能,但这些对我的应用来说并不是问题。