#24706: ModelForm._post_clean assumes that setting attributes onto an instance
cannot raise ValidationError
--------------------------------------+--------------------
     Reporter:  kezabelle             |      Owner:  nobody
         Type:  Cleanup/optimization  |     Status:  new
    Component:  Forms                 |    Version:  master
     Severity:  Normal                |   Keywords:
 Triage Stage:  Unreviewed            |  Has patch:  0
Easy pickings:  0                     |      UI/UX:  0
--------------------------------------+--------------------
 Currently,
 [https://github.com/django/django/blob/master/django/forms/models.py#L433
 ModelForm._post_clean] calls out to `construct_instance` which, under the
 hood, uses `setattr(instance, self.name, data)`, assuming that no
 cleverness is taking place, so doesn't attempt to catch `ValidationError`
 as subsequent lines do (when calling `full_clean` and `validate_unique`)

 This precludes doing things like using `__setattr__` on the model, or
 replacing raw attribute access with descriptors (which is
 [https://github.com/kezabelle/django-
 strictmodels/blob/master/strictmodels.py#L14 what I did] to encounter
 this) that do validation checks on assignment rather than remembering to
 call `full_clean` everywhere.

 As far as I can see, this is the only part of the `_post_clean`
 implementation where ValidationError could conceivably be being thrown
 which isn't already handled.

 Without catching the exception, it I think necessitates using a custom
 ModelForm subclass just to do something like:
 {{{
 class MySubForm(ModelForm):
     def _post_clean(self):
         try:
             super(MySubForm, self)._post_clean()
         except ValidationError as e:
             self._update_errors(e)
 }}}
 to avoid the following stacktrace:
 {{{
 django/forms/forms.py:184: in is_valid
     return self.is_bound and not self.errors
 django/forms/forms.py:176: in errors
     self.full_clean()
 django/forms/forms.py:394: in full_clean
     self._post_clean()
 django/forms/models.py:427: in _post_clean
     self.instance = construct_instance(self, self.instance, opts.fields,
 construct_instance_exclude)
 django/forms/models.py:62: in construct_instance
     f.save_form_data(instance, cleaned_data[f.name])
 django/db/models/fields/__init__.py:874: in save_form_data
     setattr(instance, self.name, data)
 strictmodels.py:42: in __set__
     new_value = self.field.clean(value=value, model_instance=instance)
 django/db/models/fields/__init__.py:589: in clean
     self.run_validators(value)

 ValidationError: ['error string', 'etc']
 }}}

 As you can see, the exception is the result of '''my code''', but has
 broken the ''implied'' contract of `.is_valid()` being that it suppresses
 and collects `ValidationErrors`.

--
Ticket URL: <https://code.djangoproject.com/ticket/24706>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/052.f69cb358bbe737b2572d880c69de685d%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to