Opened 10 years ago

Closed 9 months ago

#22097 closed Bug (fixed)

IntegerField with given choices leads to wrong has_changed() work

Reported by: igor.mitrenko@… 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 Claude Paroz, 10 years ago

Owner: changed from nobody to Claude Paroz
Severity: NormalRelease blocker
Status: newassigned
Triage Stage: UnreviewedAccepted

This regression was introduced in a0f3eeccf3d5a90f9914bfe20b15df05673ea59d in response to the use case described in #21397.

comment:2 by Claude Paroz <claude@…>, 10 years ago

Resolution: fixed
Status: assignedclosed

In cb844497d01ddb45603e47891cdf36ae0b006d03:

Fixed #22097 -- Fixed change detection for TypedChoiceField

Thanks Igor Mitrenko for the report.

comment:3 by GHPS, 9 months ago

Resolution: fixed
Status: closednew

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()

Last edited 9 months ago by GHPS (previous) (diff)

comment:4 by Mariusz Felisiak, 9 months ago

Resolution: fixed
Status: newclosed

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.

Note: See TracTickets for help on using tickets.
Back to Top