Метод clean

Метод clean применяется к экземпляру модели и выполняет пользовательскую валидацию данных, которая не может быть покрыта стандартными валидаторами полей. Он позволяет проверять взаимосвязи между полями, а также выполнять сложные проверки целостности данных. Метод вызывается автоматически при вызове full_clean или при сохранении модели через ModelForm. При обнаружении ошибок метод должен выбрасывать исключение ValidationError с соответствующими сообщениями.

Синтаксис

pythonclass MyModel(models.Model):
  # поля модели
  
  def clean(self):
    # пользовательская валидация
    if condition:
      raise ValidationError('Error message')

Пример

Давайте создадим модель статьи с проверкой того, что дата публикации не может быть раньше даты создания:

pythonfrom django.db import models
from django.core.exceptions import ValidationError

class Article(models.Model):
  title = models.CharField(max_length=100)
  created_at = models.DateTimeField(auto_now_add=True)
  published_at = models.DateTimeField(null=True, blank=True)
  
  def clean(self):
    if self.published_at and self.created_at:
      if self.published_at < self.created_at:
        raise ValidationError('Publication date cannot be earlier than creation date')

Теперь выполним валидацию экземпляра модели:

pythonfrom myapp.models import Article
from datetime import datetime, timedelta
from django.core.exceptions import ValidationError

article = Article(
  title='New article',
  published_at=datetime.now() - timedelta(days=1)
)
try:
  article.full_clean()
except ValidationError as e:
  print(e.message_dict)

Результат выполнения кода:

python{'__all__': ['Publication date cannot be earlier than creation date']}

Пример

Давайте добавим проверку уникальности комбинации полей 'title' и 'category':

pythonfrom django.db import models
from django.core.exceptions import ValidationError

class Article(models.Model):
  title = models.CharField(max_length=100)
  category = models.CharField(max_length=50)
  
  def clean(self):
    if Article.objects.filter(
      title=self.title,
      category=self.category
    ).exclude(pk=self.pk).exists():
      raise ValidationError('Article with this title and category already exists')

Выполним проверку на дубликат:

pythonfrom myapp.models import Article
from django.core.exceptions import ValidationError

Article.objects.create(title='Django Guide', category='Programming')
duplicate = Article(title='Django Guide', category='Programming')
try:
  duplicate.full_clean()
except ValidationError as e:
  print(e.message_dict)

Результат выполнения кода:

python{'__all__': ['Article with this title and category already exists']}

Пример

Давайте создадим модель заказа с проверкой минимальной суммы:

pythonfrom django.db import models
from django.core.exceptions import ValidationError

class Order(models.Model):
  user = models.CharField(max_length=50)
  total = models.DecimalField(max_digits=10, decimal_places=2)
  
  def clean(self):
    if self.total < 1:
      raise ValidationError({'total': 'Order total must be at least 1'})

Протестируем валидацию с некорректной суммой заказа:

pythonfrom myapp.models import Order
from django.core.exceptions import ValidationError

order = Order(user='John', total=0.50)
try:
  order.full_clean()
except ValidationError as e:
  print(e.message_dict)

Результат выполнения кода:

python{'total': ['Order total must be at least 1']}

Смотрите также

  • метод full_clean,
    который выполняет все этапы валидации модели
  • метод clean_fields,
    который валидирует отдельные поля модели
  • метод validate_unique,
    который проверяет уникальность полей модели
  • метод validate_constraints,
    который проверяет ограничения модели