Проверка полей модели Django
Где должна выполняться проверка полей модели в django?
Я мог бы назвать как минимум два возможных варианта: в перегруженном методе .save() модели или в методе .to_python() для моделей. Подкласс подкласса (очевидно, для этого нужно писать пользовательские поля).
Возможные варианты использования:
- когда совершенно необходимо гарантировать, что пустая строка не будет записана в базу данных (пустой = аргумент ключевого слова False здесь не работает, это только для проверки формы)
- когда необходимо гарантировать, что аргумент ключевого слова "choice" получает уважение на уровне db, а не только в интерфейсе администратора (тип эмуляции типа данных enum)
В моделях также есть атрибут класса empty_strings_allowed
. Определение базового класса Field и производные классы с радостью переопределяют его, однако он, похоже, не влияет на уровень базы данных, то есть я все же могу построить модель с полями с пустой строкой и сохраните ее в базе данных. Который я хочу избежать (да, это необходимо).
Возможные реализации
на уровне поля:
class CustomField(models.CharField):
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if not value:
raise IntegrityError(_('Empty string not allowed'))
return models.CharField.to_python(self, value)
на уровне модели:
class MyModel(models.Model)
FIELD1_CHOICES = ['foo', 'bar', 'baz']
field1 = models.CharField(max_length=255,
choices=[(item,item) for item in FIELD1_CHOICES])
def save(self, force_insert=False, force_update=False):
if self.field1 not in MyModel.FIELD1_CHOICES:
raise IntegrityError(_('Invalid value of field1'))
# this can, of course, be made more generic
models.Model.save(self, force_insert, force_update)
Возможно, я что-то упустил, и это можно сделать проще (и чище)?
Ответы
Ответ 1
Django имеет систему проверки модели, начиная с версии 1.2.
В комментариях sebpiq говорит: "Хорошо, теперь есть место для проверки модели... за исключением того, что она запускается только при использовании ModelForm! Поэтому вопрос остается, когда необходимо убедиться, что валидация соблюдается на db-level, что вы должны делать? Где назвать full_clean?"
Это невозможно с помощью проверки на уровне Python, чтобы гарантировать, что валидация соблюдается на уровне db. Ближайшим является, вероятно, вызов full_clean
в переопределенном методе save
. Это не делается по умолчанию, потому что это означает, что все, кто называет этот метод сохранения, теперь лучше подготовлены к захвату и обработке ValidationError
.
Но даже если вы это сделаете, кто-то все еще может обновлять экземпляры модели массово, используя queryset.update()
, который будет обходить эту проверку. Невозможно реализовать Django разумно-эффективный queryset.update()
, который все еще может выполнять проверку уровня на Python для каждого обновленного объекта.
Единственный способ действительно гарантировать целостность уровня db - через ограничения уровня db; любая проверка, которую вы выполняете с помощью ORM, требует, чтобы писатель кода приложения знал о том, когда выполняется проверка (и обрабатывать ошибки проверки).
Вот почему проверка модели по умолчанию применяется только в ModelForm
, потому что в ModelForm уже существует очевидный способ обработки ValidationError
.
Ответ 2
Я думаю, вы хотите этого →
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')
(Скопировано из http://djangosnippets.org/snippets/2319/)
Ответ 3
Корневая проблема для этого заключается в том, что валидация должна выполняться на моделях. Это довольно долго обсуждалось в django (проверка достоверности формы формы поиска в списке рассылки Dev). Это приводит либо к дублированию, либо к тому, что ускользает от проверки, прежде чем нажать db.
В то время как это не касается сундука, Malcolm "решение для проверки модели для бедных людей" , вероятно, является самым чистым решением, чтобы избежать повторения.
Ответ 4
Если я понимаю вас "четко" - вы должны переопределить функцию get_db_prep_save вместо to_python