Не думаю, что для кого-то открою Америку, сказав что в 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 потому что не вижу в этом особого смысла), может в новых версиях что-то поменялось, уточняйте в документации :)