Opened 5 years ago

Closed 5 years ago

#30596 closed Bug (fixed)

SplitArrayField.has_changed() is always True for numeric fields.

Reported by: Evgeniy Krysanov Owned by: Chason Chaffin
Component: contrib.postgres Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Code example:

# models.py
class Signal(models.Model):
    order_price = ArrayField(models.FloatField(), default=list)

# admin.py
class SignalForm(forms.ModelForm):
    order_price = SplitArrayField(base_field=forms.FloatField(), size=3, remove_trailing_nulls=True, required=False)

@admin.register(Signal)
class SignalAdmin(admin.ModelAdmin):
    form = SignalForm

    def save_model(self, request, obj, form, change):
        super().save_model(request, obj, form, change)
        print(form.has_changed())  # Always True even if no values was changed

Attachments (1)

test-30596.diff (861 bytes ) - added by Mariusz Felisiak 5 years ago.
Test.

Download all attachments as: .zip

Change History (7)

comment:1 by Mariusz Felisiak, 5 years ago

I was not able to reproduce this issue in basic tests. Maybe it is related remove_trailing_nulls=True, do you have them in a database? e.g. [1.33, 5.33. 8.78, None] Can you check initial data?

in reply to:  1 comment:2 by Evgeniy Krysanov, 5 years ago

Replying to felixxm:

I was not able to reproduce this issue in basic tests. Maybe it is related remove_trailing_nulls=True, do you have them in a database? e.g. [1.33, 5.33. 8.78, None] Can you check initial data?

I checked a lot of cases until I isolated the problem - https://github.com/django/django/blob/master/django/forms/forms.py#L451

initial_value is the original object's value, for example [3.5, 1.0, 2.2]. data_value is converted from form data to ['3.5', '1.0', '2.2'], but not converted to FloatField values. If the initial_value is [] then the data_value is ['', '', '']. Thats why has_changed always returns True.

I fixed this bug in my project with the following code:

class BetterSplitArrayField(SplitArrayField):
    def to_python(self, value):
        return self.clean(value)

When calling field.has_changed(initial_value, data_value)there is a call to data = self.to_python(data) https://github.com/django/django/blob/master/django/forms/fields.py#L181 but SplitArrayField just returns the original value from this method. My code fix this.

But it would be better to see the fix in Django source. I can write a PR with unitests if you wish.

Last edited 5 years ago by Evgeniy Krysanov (previous) (diff)

comment:3 by Mariusz Felisiak, 5 years ago

Component: Formscontrib.postgres
Summary: Form method `has_changed` always returns True if form has the SplitArrayField fieldSplitArrayField.has_changed() is always True for numeric fields.
Triage Stage: UnreviewedAccepted
Version: 2.2master

Thanks for details. I was able to reproduce this issue.

by Mariusz Felisiak, 5 years ago

Attachment: test-30596.diff added

Test.

comment:4 by Chason Chaffin, 5 years ago

Owner: changed from nobody to Chason Chaffin
Status: newassigned

comment:5 by Chason Chaffin, 5 years ago

Has patch: set

comment:6 by Mariusz Felisiak <felisiak.mariusz@…>, 5 years ago

Resolution: fixed
Status: assignedclosed

In c238e65e:

Fixed #30596 -- Fixed SplitArrayField.has_changed() for non-string base fields.

Thanks to Evgeniy Krysanov for the report and the idea to use to_python.
Thanks to Mariusz Felisiak for the test case.

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