Добавление пользовательской проверки модели Django - PullRequest
38 голосов
/ 09 сентября 2011

У меня есть модель Django с диапазоном дат начала и окончания. Я хочу включить проверку, чтобы ни у одной записи не было перекрывающихся диапазонов дат. Какой самый простой способ реализовать это, чтобы мне не приходилось повторять эту логику?

например. Я не хочу повторно реализовывать эту логику в форме и a ModelForm и в форме администратора и переопределенной модели save().

Насколько я знаю, Django не облегчает глобальное применение этих типов критериев.

Поиск в Google не очень помог, поскольку «проверка модели» обычно относится к проверке конкретных полей модели, а не всего содержимого модели или отношений между полями.

Ответы [ 3 ]

45 голосов
/ 18 сентября 2013

Основной шаблон, который я нашел полезным, - поместить всю мою пользовательскую проверку в 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)

Это не сделано по умолчанию, как объяснено здесь , потому что это мешает определенным функциям, но это не проблема для моего приложения.

20 голосов
/ 09 сентября 2011

Я бы переопределил метод 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 есть хорошее обсуждение простого, правильного условия перекрытия диапазона .

10 голосов
/ 09 сентября 2011

Я думаю, вы должны использовать это: 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()
...