Этот топик - заметки в этапах ковыряния в исходниках и понятие структуры (может быть ошибочное мнение).
Итак,
1. форма содержит поля (fields).
2. в полях есть виджеты (widgets).
Инициализация формы:
Для вывода формы (а точнее преобразования значения поля из запроса в вид на форме), а также для обратного преобразования этих данных используются виджеты (которые используют данные instance или initial поля, а также POST данные).
Для проверки POST данных, преобразованных виджетом, используется метод clean поля (см ниже).
Вспомним, как мы сохраняем форму. Примерный процесс проверки POST данных:
- if request.method == 'POST': # If the form has been submitted...
- if form.is_valid():
- # Process the data in form.cleaned_data
- # ...
- form.save()
Наша форма имеет предка - класс BaseForm, в котором есть функция is_valid():
- def is_valid(self):
- """
- Returns True if the form has no errors. Otherwise, False. If errors are
- being ignored, returns False.
- """
- return self.is_bound and not bool(self.errors)
Свойство is_bound:
- self.is_bound = data is not None or files is not None
self.errors:
- errors = property(_get_errors)
Метод свойства:
- def _get_errors(self):
- "Returns an ErrorDict for the data provided for the form"
- if self._errors is None:
- self.full_clean()
- return self._errors
А вот сама функция
- def full_clean(self):
- """
- Cleans all of self.data and populates self._errors and
- self.cleaned_data.
- """
- self._errors = ErrorDict()
- if not self.is_bound: # Stop further processing.
- return
- self.cleaned_data = {}
- # If the form is permitted to be empty, and none of the form data has
- # changed from the initial data, short circuit any validation.
- if self.empty_permitted and not self.has_changed():
- return
- for name, field in self.fields.items():
- # value_from_datadict() gets the data from the data dictionaries.
- # Each widget type knows how to retrieve its own data, because some
- # widgets split data over several HTML fields.
- value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
- try:
- if isinstance(field, FileField):
- initial = self.initial.get(name, field.initial)
- value = field.clean(value, initial)
- else:
- value = field.clean(value)
- self.cleaned_data[name] = value
- if hasattr(self, 'clean_%s' % name):
- value = getattr(self, 'clean_%s' % name)()
- self.cleaned_data[name] = value
- except ValidationError, e:
- self._errors[name] = e.messages
- if name in self.cleaned_data:
- del self.cleaned_data[name]
- try:
- self.cleaned_data = self.clean()
- except ValidationError, e:
- self._errors[NON_FIELD_ERRORS] = e.messages
- if self._errors:
- delattr(self, 'cleaned_data')
Метод clean поля:
- class Field(object):
- def clean(self, value):
- """
- Validates the given value and returns its "cleaned" value as an
- appropriate Python object.
- Raises ValidationError for any errors.
- """
- if self.required and value in EMPTY_VALUES:
- raise ValidationError(self.error_messages['required'])
- return value
Например, для поля CharField
- def clean(self, value):
- "Validates max_length and min_length. Returns a Unicode object."
- super(CharField, self).clean(value)
- if value in EMPTY_VALUES:
- return u''
- value = smart_unicode(value)
- value_length = len(value)
- if self.max_length is not None and value_length > self.max_length:
- raise ValidationError(self.error_messages['max_length'] % {'max': self.max_length, 'length': value_length})
- if self.min_length is not None and value_length < self.min_length:
- raise ValidationError(self.error_messages['min_length'] % {'min': self.min_length, 'length': value_length})
- return value
Теперь становится более понятным, в каком месте лучше провести свою проверку или преобразовать данные с формы, переопределяя класс поля или создав свой виджет.
Моя задача: передать для одного поля ModelChoiceField динамический queryset, в зависимости от другого значения в POST данных (это нужно из-за того, что поле ModelChoiceField меняется динамически через AJAX, в зависимости от другого значения).
Изучив вышеприведенный код я нашел несколько путей для реализации:
1. Переопределив метод clean формы forms.ModelChoiceField
2. Передать в поле полный queryset, изменив виджет (чтобы он принимал наш "подставной" queryset), для вывода ограниченного числа значений.
3. Передавать нужный queryset в зависимости от data (то есть POST).
Последний вариант самый предпочтительный - самый простой, плюс доп проверка queryset, в зависимости от поля (сохранится структура базы) и у меня работает)
Комментариев нет:
Отправить комментарий