Opened 17 months ago
Closed 17 months ago
#34711 closed New feature (wontfix)
Make ChoiceField auto-detect and coerce values.
Reported by: | GHPS | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 4.2 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Summary:
While trying to find out why the forms in two different applications are
marked for saving even if no field has been changed, I could pin the problem down
to the integer enum fields used in both models and form.ChoiceField involved.
In short, form.has_changed() is triggered because of a double data conversion
during the round-trip.
The value of the integer enum field is converted to a string and rendered.
The POST request comes back with this string which gets compared to the original
enum integer which shows that they are different. During the - unnecessary - saving
the string becomes an int again - which is why the original problem is hidden
from the perspective of a user.
This bug seem to be a regression from bug #22097.
Background:
In Django 4.2 on Linux (Kubuntu 22.04) take the following enum and
create a model field in model.py:
class eAssets(models.IntegerChoices): dvd =1, _('DVD') bluray =3, _('Blu-ray') none =0, _('-') eAsset=models.PositiveSmallIntegerField(choices=eAssets.choices, default=eAssets.none)
Create a choice form field in form.py:
eAsset=forms.ChoiceField(label=mdMovie._meta.get_field('eAsset').verbose_name, choices=[(dcEntry.value, dcEntry.label) for dcEntry in mdMovie.eAssets], initial=mdMovie.eAssets.none)
After processing the POST request, form.has_changed() is always true in view.py:
>fmForm=fmMovie(vRequest.POST, vRequest.FILES or None, instance=rcCurrentMovie) >fmForm.has_changed() True >fmForm.changed_data ['eAsset']
Links:
- I've found an old ticket #22097 and it looks to me like this nine year old bug has shown its ugly face again.
- On Stackoverflow someone reported the same problem.
dynamic integer model choice field always fails form.has_changed()
Change History (9)
comment:1 by , 17 months ago
Description: | modified (diff) |
---|
comment:2 by , 17 months ago
Resolution: | → needsinfo |
---|---|
Status: | new → closed |
comment:3 by , 17 months ago
As suggested I completely started from scratch[1].
The project is as barebone as it can be.
But the problem still remains - form.has_changed() is alway True for
IntegerChoice enums. Even for different browsers (Firefox/Chrome) or
platforms (Linux/Android)
The sample code is ready to run: The database has three entries which
can be viewed under local URL /test/[1,2,3]/. Pressing the save button
will always trigger the message that the eAsset field has been changed.
comment:4 by , 17 months ago
Resolution: | needsinfo |
---|---|
Status: | closed → new |
comment:5 by , 17 months ago
Hi GHPS,
Thanks for taking the time to setup a small test project, though unfortunately the issue isn't with Django in this case; it's with the sample project's code.
If you want to manually override model form fields you must be sure to use the correct form field type. In the case of an IntegerField
with choices
you must use a TypedChoiceField A model form will use these automatically for an IntegerField
by default.
Regards,
David
follow-up: 7 comment:6 by , 17 months ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
comment:7 by , 17 months ago
Resolution: | invalid |
---|---|
Status: | closed → new |
Sorry for reopening this issue again...
Replying to David Sanders:
If you want to manually override model form fields you must be sure to use the correct form field type. In the case of an IntegerField with choices you must use a TypedChoiceField A model form will use these automatically for an IntegerField by default.
Thanks for the suggestion which I checked on the small test project mentioned above.
So I've changed the form field type from ChoiceField to TypedCoiceField.
eAsset=forms.TypedChoiceField(choices=sampleModel.eAssets.choices)
This change, however, did nothing to the original problem: The form.has_changed() flag is alway True
regardless of changing or not changing any field in the form.
In the next step I completely removed said line and therefore relied on the
form field automatically generated - voila, no problem.
Seemingly the ChoiceField/TypedChoiceField methods convert the outgoing
and incoming choice values which confuses the form.has_changed() detection.
comment:8 by , 17 months ago
Update:
Setting coerce=int in TypeChoiceField works as well but seems to me a rather crude way to treat
the original problem.
With this approach the developer is forced to remember the right field type (TypeChoiceField) and
involved values (ints) to clamp a conversion mechanism hidden behind the scene down to ground.
In both, the enum (IntegerChoice) and the model (PositiveSmallIntegerField) the information is available
that we are dealing with integers. Perhaps this information can be utilized to let ChoiceField do
the right thing without intervention...
comment:9 by , 17 months ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
Summary: | form.has_changed() is always True for IntegerChoices Enum Model Field → Make ChoiceField auto-detect and coerce values. |
Type: | Bug → New feature |
I appreciate you'd like to reopen the ticket, but we will not change this long-standing behavior and incorporate TypedChoiceField
logic in ChoiceField
. If you don't agree, please follow the triaging guidelines with regards to wontfix tickets and take this to DevelopersMailingList.
Hello,
I have tried to reproduce your issue, and since the provided code is incomplete, I built my own models and tests. But, in my testing, if request.POST has the correct and complete information for the model instance,
has_changed
correctly reports False.So, I'll be closing this report as
needsinfo
since we need more information to properly triage this ticket. Specifically, we'd need a django test project with the necessary models and forms, plus instructions in how to trigger the issue.Could you please provide that? Thanks!