Проверка полей модели Django

Куда должна идти проверка полей модели в django?

Я мог бы назвать по крайней мере два возможных варианта: в перегруженном методе .save () модели или в методе .to_python () подкласса models.Field (очевидно, чтобы это работало, вы должны написать настраиваемые поля).

Возможные варианты использования:

  • когда абсолютно необходимо убедиться, что пустая строка не записывается в базу данных (аргумент ключевого слова blank = False здесь не работает, он предназначен только для проверки формы)
  • когда необходимо убедиться, что аргумент ключевого слова "choices" соблюдается на уровне базы данных, а не только в интерфейсе администратора (своего рода имитация типа данных enum)

В моделях также есть атрибут уровня класса empty_strings_allowed. Определение базового класса поля и производные классы успешно переопределяют его, однако он, похоже, не оказывает никакого влияния на уровень базы данных, что означает, что я все еще могу построить модель с пустой строкой поля и сохраните его в базе данных. Чего я хочу избежать (да, это необходимо).

Возможные реализации:

на полевом уровне:

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)

Возможно, мне чего-то не хватает, и это можно сделать проще (и чище)?


person shylent    schedule 26.10.2009    source источник


Ответы (4)


Начиная с версии Django имеет систему проверки модели 1.2.

В комментариях sebpiq говорит: «Хорошо, теперь есть место для проверки модели ... за исключением того, что она запускается только при использовании ModelForm! Таким образом, остается вопрос, когда необходимо обеспечить соблюдение проверки на уровне базы данных. , что делать? Куда звонить full_clean? "

Невозможно с помощью проверки на уровне Python гарантировать, что проверка соблюдается на уровне базы данных. Ближайшим, вероятно, является вызов full_clean в замещенном методе save. По умолчанию это не делается, потому что это означает, что теперь всем, кто вызывает этот метод сохранения, лучше подготовиться к тому, чтобы поймать и обработать ValidationError.

Но даже если вы это сделаете, кто-то все равно сможет массово обновлять экземпляры модели, используя queryset.update(), что позволит обойти эту проверку. Django не может реализовать достаточно эффективный queryset.update(), который по-прежнему мог бы выполнять проверку на уровне Python для каждого обновленного объекта.

Единственный способ действительно гарантировать целостность уровня базы данных - это ограничения уровня базы данных; любая проверка, которую вы выполняете через ORM, требует, чтобы автор кода приложения знал, когда проверка выполняется (и обрабатывала сбои проверки).

Вот почему проверка модели по умолчанию применяется только в ModelForm - потому что в ModelForm уже есть очевидный способ обработки ValidationError.

person Carl Meyer    schedule 26.10.2009
comment
Отлично, здорово, это просто здорово! Я бегло просмотрел исходный код (кстати, есть ли способ получить доступ к документации, поскольку это можно сделать с помощью транка?), И мне кажется, что это именно то, что мне нужно. Я имею в виду, что я полностью за то, чтобы катить свой собственный, но django превосходно предоставляет единообразный способ делать что-то (ну, во всяком случае, IMO). - person shylent; 26.10.2009
comment
Проверьте эту ветку, убедитесь, что у вас установлены Docutils и Sphinx, затем перейдите в каталог docs / и запустите make html. Это должно создать документы в форме HTML, как и на веб-сайте Django, и вы сможете получить к ним доступ локально. - person Carl Meyer; 26.10.2009
comment
Хорошо, я вернулся к чтению источника (в частности, models/fields/__init__.py, models / base.py и core / validators.py), поскольку документация на данный момент ничего не говорит о проверке модели. Однако следует отметить, что он работает почти так же, как и проверка форм (по крайней мере, общая логика более или менее такая же). Во всяком случае, это то, что я искал. Я просто надеюсь, что мои приложения не сломаются ужасно, если я просто переключусь с магистрали на эту ветку. - person shylent; 27.10.2009
comment
Конечно, не должно ломать ваши приложения! Если это так, сообщите об этом как об ошибке, и вы окажете всем нам услугу. Не осознавал, что документов еще не было, я уверен, что это в списке задач, прежде чем он будет объединен; может ты хочешь их написать? ;-) - person Carl Meyer; 27.10.2009
comment
Я определенно хотел бы помочь django. И из-за отсутствия документации мне пришлось прибегнуть к изучению кода, что, в случае с django, вовсе не является неприятным опытом, и это дало мне твердое представление о том, как все работает (хотя для этого потребовалось некоторое время). время). Кстати, на самом деле ничего не сломалось, и часть проверки, безусловно, работает. - person shylent; 29.10.2009
comment
Он все еще не отвечает на вопрос ... хорошо, теперь есть место для проверки модели ... за исключением того, что он запускается только при использовании ModelForm !!! Итак, остается вопрос, когда необходимо обеспечить соблюдение [любой проверки] на уровне базы данных, что делать? Куда позвонить full_clean? - person sebpiq; 17.02.2012

Я думаю, ты хочешь этого ->

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/)

person Darius    schedule 03.03.2011
comment
Нет, не совсем. проверка модели на данный момент находится в стабильном django, поэтому суть исходного вопроса является спорной - person shylent; 03.03.2011
comment
Я так не думаю, потому что валидаторы не запускаются при сохранении, поэтому они позволяют добавлять вещи, которые не проходят валидацию. Из docs.djangoproject.com/en/dev/releases/1.2/ # model-validation Простой вызов метода save () экземпляра модели не приведет к проверке данных экземпляра. Хотя, возможно, я неправильно понимаю, чего хочет OP, однако я определенно хотел бы, чтобы save () проверял вещи :) - person Darius; 03.03.2011

Основная проблема заключается в том, что проверка должна выполняться на моделях. Это уже довольно давно обсуждается в django (проверка с учетом модели поисковой формы в списке рассылки разработчиков). Это приводит либо к дублированию, либо к тому, что что-то ускользает от проверки до попадания в db.

Хотя это не касается ствола, Малкольм " решение для проверки модели для бедняков ", вероятно, самое чистое решение, позволяющее избежать повторений.

person Arthur Debert    schedule 26.10.2009
comment
Я прочитал его содержимое в кеше Google. В этом есть смысл, да. Это не очень полезно, если я не собираюсь использовать формы (не нужны ли формы для ввода данных, верно?), Однако это, безусловно, способ избежать повторения себя. - person shylent; 26.10.2009

Если я вас "четко" понимаю - вы должны переопределить функцию get_db_prep_save вместо to_python

person Oduvan    schedule 26.10.2009
comment
На самом деле это хорошая идея. Однако я специально упомянул .to_python (), потому что он запускается после инициализации поля (если вы указываете metaclass = models.SubfieldBase), поэтому проверка выполняется раньше, что означает, что вы даже не можете инициализировать модель, если вы передаете неверные значения для полей. Хотя, возможно, твой путь правильный. По крайней мере, для меня это имеет смысл. - person shylent; 26.10.2009