#33106: ModelMultipleChoiceField clean() method calls prepare_value instead of
to_python
--------------------------------------+------------------------
Reporter: Adam McKay | Owner: nobody
Type: Bug | Status: new
Component: Forms | Version: 3.2
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
--------------------------------------+------------------------
The [https://docs.djangoproject.com/en/3.2/ref/forms/validation/#form-and-
field-validation Form and field validation documentation] says:
The clean() method on a Field subclass is responsible for running
to_python(), validate(), and run_validators() in the correct order and
propagating their errors.
However the `clean()` method of `ModelMultipleChoiceField` calls `value =
self.prepare_value(value)` which is causing issues for my use case of
hiding primary keys on forms using a [https://hashids.org/ hashids] module
as I am subclassing the `to_python` and `prepare_values` methods to
ensure the `pk` exposed to users are encoded/decoded as appropriate,
however the `to_python` method is not called as an error `“encodedValue”
is not a valid value.` is returned to the user because the value is not
correctly decoded when it is checked as a valid choice.
I have written a test case for
`tests/model_forms/test_modelchoicefield.py` in `ModelChoiceFieldTests`
which for this test merely adds `42` to the `pk` before exposing it to the
user to demonstrate the behaviour:
{{{
def test_clean_serializes_input(self):
class
EncryptedModelMultipleChoiceField(forms.ModelMultipleChoiceField):
"""Hide pk by modifying by 42"""
def to_python(self, value):
if not value:
return []
if hasattr(value, '__iter__'):
return [int(getattr(v, 'pk', v)) - 42 for v in value]
return int(getattr(value, 'pk', value)) - 42
def prepare_value(self, value):
if not value:
return []
if hasattr(value, '__iter__'):
return [int(getattr(v, 'pk', v)) + 42 for v in value]
return int(getattr(value, 'pk', value)) + 42
f = EncryptedModelMultipleChoiceField(Category.objects.all())
print(f.widget.render('name', []),)
self.assertHTMLEqual(
f.widget.render('name', []),
"""<select name="name" multiple>
<option value="%s">Entertainment</option>
<option value="%s">A test</option>
<option value="%s">Third</option>
</select>""" % (self.c1.pk + 42, self.c2.pk + 42, self.c3.pk +
42),
)
with self.assertRaises(ValidationError):
f.clean('')
with self.assertRaises(ValidationError):
f.clean(None)
with self.assertRaises(ValidationError):
f.clean(0)
self.assertEqual(['Entertainment'], [c.name for c in
f.clean([self.c1.pk + 42])])
self.assertEqual(['Entertainment', 'Third'], [c.name for c in
f.clean([self.c1.pk + 42, self.c3.pk + 42])])
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/33106>
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 view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/051.af739002c82c97238ba84dab8e870931%40djangoproject.com.