Не думаю, что для кого-то открою Америку, сказав что в Django есть валидация не только для форм, а и для моделей :) Однако судя по моей практике это чуть спорное, но весьма полезное решение не спешат повсеместно использовать и городят свои велосипеды для проверки значений при создании/обновлении моделей. Поэтому хочу поделиться несколькими простыми советами по этой теме.
Во-первых, храните валидаторы отдельно от моделей. Хранение всех валидаторов в models.py неимоверно раздувает и без того не маленький модуль моделей (а иногда это и пакет), хранение же валидаторов в validators.py решает эту проблему и логично выносит все операции по проверке значений/уникальности модели в подходящее для этого место. Ну и сюда же, не пишите сложные проверки в Model.clean, Model.validate_unique методах, просто вызывайте необходимые функции валидации из validators.py, например:
models.py
from django.db import models
from .validators import validate_complex_case, validate_unique
class Card(models.Model):
...
def clean(self):
validate_complex_case(instance)
def validate_unique(self, exclude=None):
validate_unique(self)
return super(Card, self).validate_unique(exclude)
validators.py
from django.core.exceptions import ValidationError
def validate_complex_case(instance):
if instance.name == 'XXX' and instance.path != '/path/to/XXX':
raise ValidationError('Please provide proper path for the card.')
def validate_unique(instance):
manager = instance._default_manager
other = manager.filter(name=instance.name)
if other.count():
message = 'Card with this Name already exists.'
raise ValidationError({'__all__': [message]})
Во-вторых, непонятно зачем, но ошибки в проверке на уникальность модели нужно обворачивать в message_dict, использование простого сообщения приводит к AttributeError. Хотя с другой стороны это повышает возможности по написанию ошибок в разных полях моделей. Можно, например, подсветить какие именно поля не уникальны и уже сохранены в БД.
В-третьих, не забывайте, что валидаторов может быть сколько угодно много. Старайтесь разбивать большие валидаторы на маленькие атомарные функции и выносить их в core.validators или project.validators, чтоб иметь возможность использовать их не только для текущего приложения, а и для любых других приложений в проекте. Аттрибут validators у поля модели принимает список валидаторов, так что Вы вполне можете писать,
from django.core.validators import MaxLengthValidator, MinLengthValidator
from django.db import models
from .validators import validate_markup
class Card(models.Model):
...
comment = models.TextField(validators=[
MinLengthValidator(10), MaxLengthValidator(1000), validate_markup
])
В-четвертых, валидация модели автоматически не вызывается, когда вы хотите сохранить модель не из админ-панели или ModelForm'ы. Т.е., просто добавление валидаторов в поля модели и задание методов clean или validate_unique не гарантирует, что неправильные данные не сохранятся в базе данных после Model.objects.create или instance.save(). Для того, чтобы обезопасить себя и включить валидацию и для моделей, используйте сигналы:
from django.db import models
from django.db.models import signals
from django.dispatch import receiver
class Card(models.Model)
...
@receiver(signals.pre_save, sender=Card)
def validate_card(instance):
instance.full_clean()
Ну и в-пятых,
- Не забывайте про стандартные валидаторы Django
- Импортируйте исключение ошибки валидации как
from django.core.exceptions import ValidationError - Для моделей нельзя писать методы
clean_FIELDкак для форм - Валидация модели при вызове
instance.full_clean()идет в следующей последовательности: валидация полей (instance.clean_fields()), вызовinstance.clean(), проверка на уникальность (instance.validate_unique())
зы. Код и принципы данного материала актуальны для Django 1.3 ветки (да я так и не пересел на 1.4 или 1.5 потому что не вижу в этом особого смысла), может в новых версиях что-то поменялось, уточняйте в документации :)