Opened 11 years ago
Closed 18 months ago
#22097 closed Bug (fixed)
IntegerField with given choices leads to wrong has_changed() work
Reported by: | Owned by: | Claude Paroz | |
---|---|---|---|
Component: | Forms | Version: | 1.7-alpha-1 |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
It seems that providing choices to IntegerField makes ModelForm (created by admin, for example) to use ChoiceField and call it's to_python method, which return string. Finally, _has_changed method compares initial value (of type int) to new value (of type unicode), and always returns True.
My code sending "object created" signals on each inline object change, so it leads to creation of unnecessary "changed" events logging.
Snippet:
... class PhoneNumber(models.Model): """ Phone number set for workers """ MOBILE_PHONE = 0 WORK_PHONE = 1 HOME_PHONE = 2 KIND_CHOICES = ( (MOBILE_PHONE, _('Mobile')), (WORK_PHONE, _('Work')), (HOME_PHONE, pgettext('kind of phone number', 'Home')), ) worker = models.ForeignKey(Worker) phone_number = models.CharField(_('phone number'), max_length=12) kind = models.SmallIntegerField(_('kind of number'), choices=KIND_CHOICES, default=MOBILE_PHONE) ...
ModelForm derived from such a model created ChoiceField for kind, but it's has_changed method return True always, cause it's (as example) always 1 != u'1'
Removing choices argument is a workaround, but it breaks desired behaviour.
Another workaround is to use CharField, but the problem still remains.
Django version 1.7.dev20131210091409
Change History (4)
comment:1 by , 11 years ago
Owner: | changed from | to
---|---|
Severity: | Normal → Release blocker |
Status: | new → assigned |
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:3 by , 18 months ago
Resolution: | fixed |
---|---|
Status: | closed → new |
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.
I found this old ticket and it looks to me like the nine year old bug has
shown its ugly face again.
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:
On Stackoverflow someone reported the same problem.
dynamic integer model choice field always fails form.has_changed()
comment:4 by , 18 months ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
GHPS, please don't reopen old already fixed issue. If you believe it's an issue in Django, then please create a new ticket in Trac and follow our bug reporting guidelines.
This regression was introduced in a0f3eeccf3d5a90f9914bfe20b15df05673ea59d in response to the use case described in #21397.