Opened 6 years ago
Closed 6 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)
Change History (7)
follow-up: 2 comment:1 by , 6 years ago
comment:2 by , 6 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 return 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.
comment:3 by , 6 years ago
| Component: | Forms → contrib.postgres | 
|---|---|
| Summary: | Form method `has_changed` always returns True if form has the SplitArrayField field → SplitArrayField.has_changed() is always True for numeric fields. | 
| Triage Stage: | Unreviewed → Accepted | 
| Version: | 2.2 → master | 
Thanks for details. I was able to reproduce this issue.
comment:4 by , 6 years ago
| Owner: | changed from to | 
|---|---|
| Status: | new → assigned | 
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?