Это способ проверки полей модели Django?
Как я понимаю, при создании приложения Django данные проверяются формой до того, как они вставлены в экземпляр модели, который затем записывается в базу данных. Но если я хочу создать дополнительный уровень защиты на уровне модели данных, это то, что я сделал под текущей "лучшей практикой"? Я пытаюсь гарантировать, что имя рецензента не может быть опущено или оставлено пустым. Должен ли я помещать какую-либо специальную проверку в "чистый" метод, как я здесь делал, а затем "сохранить" вызов "full_clean", который вызывает "чистый"? Если нет, то какой предпочтительный метод? Спасибо.
class Reviewer(models.Model):
name = models.CharField(max_length=128, default=None)
def clean(self, *args, **kwargs):
if self.name == '':
raise ValidationError('Reviewer name cannot be blank')
super(Reviewer, self).clean(*args, **kwargs)
def full_clean(self, *args, **kwargs):
return self.clean(*args, **kwargs)
def save(self, *args, **kwargs):
self.full_clean()
super(Reviewer, self).save(*args, **kwargs)
Ответы
Ответ 1
Во-первых, вы не должны переопределять full_clean
, как вы это делали. Из django docs на full_clean:
Model.full_clean(exclude=None)
Этот метод вызывает Model.clean_fields()
, Model.clean()
и Model.validate_unique()
в этом порядке и вызывает a ValidationError
, у которого есть атрибут message_dict
, содержащий ошибки со всех трех этапов.
Итак, метод full_clean
уже вызывает clean
, но, переопределив его, вы запретили ему вызывать другие два метода.
Во-вторых, вызов full_clean
в методе save
является компромиссом. Обратите внимание, что full_clean
уже вызывается, когда формы модели проверяются, например. в администраторе Django. Поэтому, если вы вызываете full_clean
в методе save
, тогда метод будет выполняться дважды.
Как правило, для метода сохранения не рекомендуется повышать ошибку проверки, кто-то может вызвать save
и не поймать полученную ошибку. Однако мне нравится, что вы вызываете full_clean
, а не выполняете проверку в самом методе сохранения - этот подход позволяет модельным формам сначала поймать проблему.
Наконец, ваш метод clean
будет работать, но вы действительно сможете обработать свой пример в самом поле модели. Определите свой CharField
как
name = models.CharField(max_length=128)
Параметр blank
по умолчанию будет False. Если поле пустое, a ValidationError
будет поднято при запуске full_clean
. Помещение default=None
в ваш CharField
не наносит никакого вреда, но это немного запутанно, если вы фактически не разрешаете None
как значение.
Ответ 2
Подумав об ответах Alasdair и делая дополнительное чтение, теперь я чувствую, что модели Django не были разработаны так, чтобы их проверяли только на основе модели, как я пытаюсь сделать. Такая проверка может быть выполнена, но по себестоимости и влечет за собой использование методов валидации способами, для которых они не были предназначены.
Вместо этого теперь я считаю, что любые ограничения, отличные от тех, которые могут быть введены непосредственно в объявления полей модели (например, "unique = True" ), должны выполняться как часть проверки формы или ModelForm. Если вы хотите защитить от ввода неверных данных в базу данных проекта с помощью любых других средств (например, через ORM во время работы с интерпретатором Python), то проверка должна проводиться внутри самой базы данных. Таким образом, проверка может быть реализована на трех уровнях: 1) во-первых, реализовать все ограничения и триггеры через DDL в базе данных; 2) Внедрение любых ограничений, доступных для ваших полей модели (например, "unique = True" ); и 3) Внедрить все другие ограничения и проверки, которые отражают ваши ограничения и триггеры уровня базы данных в ваших Формах и ModelForms. При таком подходе любые ошибки проверки формы могут быть повторно отображены пользователю. И если программист напрямую взаимодействует с базой данных через ORM, он/она будет видеть исключения базы данных напрямую.
Мысли кого-нибудь?
Ответ 3
Захват сигналов предварительной записи на моих моделях гарантирует, что очистка будет вызываться автоматически.
from django.db.models.signals import pre_save
def validate_model(sender, **kwargs):
if 'raw' in kwargs and not kwargs['raw']:
kwargs['instance'].full_clean()
pre_save.connect(validate_model, dispatch_uid='validate_models')