Opened 3 years ago

Closed 3 years ago

#32625 closed Uncategorized (invalid)

JSONField with callable default reports ModelForm.has_changed() when it hasn't

Reported by: Stuart Kelly Owned by: nobody
Component: Forms Version: 3.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

The issue is similar to this bug that has been fixed: https://code.djangoproject.com/ticket/24428 however isn't due to coercion

test case to reproduce (in a new django project/app)

from django.db import models
from django.forms import ModelForm


class Vehicle(models.Model):
    modifications = models.JSONField(default=list, blank=True, null=True)


class VehicleForm(ModelForm):
    class Meta:
        model = Vehicle
        fields = ("modifications", )


def test_vehicle_form():
    vehicle = Vehicle.objects.create()
    assert vehicle.modifications == []
    data = {"modifications": "[]"}
    form = VehicleForm(data, instance=vehicle)
    assert form.is_valid()
    assert not form.has_changed()

I would expect that test to pass, but it doesn't. I'm not sure exactly where the error lies, but I have discovered the following:

field.show_hidden_initial == True
field.to_python(hidden_widget.value_from_datadict(self.data, self.files, initial_prefixed_name)) == None

which then fails the check in field.has_changed

initial_value = initial if initial is not None else ''
data_value = data if data is not None else ''
return initial_value != data_value

because data == []

Change History (1)

comment:1 by Mariusz Felisiak, 3 years ago

Component: UncategorizedForms
Resolution: invalid
Status: newclosed

Fields with callable defaults use show_hidden_initial (renders a hidden widget with initial value after the widget), that's why you need to pass initial data to forms, e.g.

>>> data = {'modifications': '[]', 'initial-modifications': '[]'}
>>> form = VehicleForm(data, instance=vehicle)
>>> form.has_changed()
False
Note: See TracTickets for help on using tickets.
Back to Top