Добавление пользовательской проверки модели Django
У меня есть модель Django с диапазоном дат начала и окончания. Я хочу принудительно провести проверку, чтобы никакие две записи не перекрывали диапазоны дат. Какой самый простой способ реализовать это, чтобы мне не пришлось повторять, что я пишу эту логику?
например. Я не хочу повторно реализовывать эту логику в форме и ModelForm
, а также в форме администратора и переопределенной модели save()
.
Насколько я знаю, Django не позволяет глобально применять эти типы критериев.
Googling не очень полезен, поскольку "проверка модели" обычно относится к проверке определенных полей модели, а не ко всему содержимому модели или отношениям между полями.
Ответы
Ответ 1
Базовый шаблон, который я нашел полезным, - это поставить все мои собственные проверки в clean()
, а затем просто вызвать full_clean()
( который вызывает clean()
и несколько других методов) изнутри save()
, например:
class BaseModel(models.Model):
def clean(self, *args, **kwargs):
# add custom validation here
super(BaseModel, self).clean(*args, **kwargs)
def save(self, *args, **kwargs):
self.full_clean()
super(BaseModel, self).save(*args, **kwargs)
Это не делается по умолчанию, как описано здесь, поскольку это мешает определенным функциям, но это не проблема для моего приложения.
Ответ 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 имеет приятное обсуждение простого, правильного диапазона перекрытия условий.
Ответ 3
Я думаю, вы должны использовать это:
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()