diff --git a/django/forms/models.py b/django/forms/models.py index 4cda4e534e..09be448984 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -23,6 +23,7 @@ from django.forms.widgets import ( SelectMultiple, ) from django.utils.choices import BaseChoiceIterator +from django.utils.hashable import make_hashable from django.utils.text import capfirst, get_text_list from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ @@ -834,8 +835,8 @@ class BaseModelFormSet(BaseFormSet, AltersData): ( d._get_pk_val() if hasattr(d, "_get_pk_val") - # Prevent "unhashable type: list" errors later on. - else tuple(d) if isinstance(d, list) else d + # Prevent "unhashable type" errors later on. + else make_hashable(d) ) for d in row_data ) diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index f78772da56..e5c026cee6 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -1703,6 +1703,30 @@ class ModelFormsetTest(TestCase): [{}, {"__all__": ["Please correct the duplicate values below."]}, {}], ) + def test_inlineformset_with_jsonfield(self): + class BookForm(forms.ModelForm): + title = forms.JSONField() + + class Meta: + model = Book + fields = ("title",) + + BookFormSet = inlineformset_factory(Author, Book, form=BookForm) + data = { + "book_set-TOTAL_FORMS": "3", + "book_set-INITIAL_FORMS": "0", + "book_set-MAX_NUM_FORMS": "", + "book_set-0-title": {"test1": "test2"}, + "book_set-1-title": {"test1": "test2"}, + "book_set-2-title": {"test3": "test4"}, + } + author = Author.objects.create(name="test") + formset = BookFormSet(data, instance=author) + self.assertEqual( + formset.errors, + [{}, {"__all__": ["Please correct the duplicate values below."]}, {}], + ) + def test_model_formset_with_custom_pk(self): # a formset for a Model that has a custom primary key that still needs to be # added to the formset automatically