From 9bc4d90d1a8c4d1dee54115a0995a7b6fca8635b Mon Sep 17 00:00:00 2001 From: Tomer Chachamu Date: Fri, 24 Nov 2017 15:48:15 +0000 Subject: [PATCH] Fixed #14642 -- Fixed generic inline formsets crash when using save_as_new=True. --- django/contrib/contenttypes/forms.py | 8 +++++- tests/generic_relations/test_forms.py | 37 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/django/contrib/contenttypes/forms.py b/django/contrib/contenttypes/forms.py index d4c3313240..de1fcaf4bb 100644 --- a/django/contrib/contenttypes/forms.py +++ b/django/contrib/contenttypes/forms.py @@ -9,7 +9,7 @@ class BaseGenericInlineFormSet(BaseModelFormSet): A formset for generic inline objects to a parent. """ - def __init__(self, data=None, files=None, instance=None, save_as_new=None, + def __init__(self, data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None, **kwargs): opts = self.model._meta self.instance = instance @@ -17,6 +17,7 @@ class BaseGenericInlineFormSet(BaseModelFormSet): opts.app_label + '-' + opts.model_name + '-' + self.ct_field.name + '-' + self.ct_fk_field.name ) + self.save_as_new = save_as_new if self.instance is None or self.instance.pk is None: qs = self.model._default_manager.none() else: @@ -29,6 +30,11 @@ class BaseGenericInlineFormSet(BaseModelFormSet): }) super().__init__(queryset=qs, data=data, files=files, prefix=prefix, **kwargs) + def initial_form_count(self): + if self.save_as_new: + return 0 + return super().initial_form_count() + @classmethod def get_default_prefix(cls): opts = cls.model._meta diff --git a/tests/generic_relations/test_forms.py b/tests/generic_relations/test_forms.py index bed31034be..99ae882693 100644 --- a/tests/generic_relations/test_forms.py +++ b/tests/generic_relations/test_forms.py @@ -200,3 +200,40 @@ id="id_generic_relations-taggeditem-content_type-object_id-1-id" />

""" % tag self.assertTrue(formset.is_valid()) new_obj, = formset.save() self.assertNotIsInstance(new_obj.obj, ProxyRelatedModel) + + def test_initial_count(self): + GenericFormSet = generic_inlineformset_factory(TaggedItem) + data = { + 'form-TOTAL_FORMS': '3', + 'form-INITIAL_FORMS': '3', + 'form-MAX_NUM_FORMS': '', + } + formset = GenericFormSet(data=data, prefix='form') + self.assertEqual(formset.initial_form_count(), 3) + formset = GenericFormSet(data=data, prefix='form', save_as_new=True) + self.assertEqual(formset.initial_form_count(), 0) + + def test_save_as_new(self): + """ + The save_as_new parameter creates new items that are associated with + the object. + """ + lion = Animal.objects.create(common_name='Lion', latin_name='Panthera leo') + yellow = lion.tags.create(tag='yellow') + hairy = lion.tags.create(tag='hairy') + GenericFormSet = generic_inlineformset_factory(TaggedItem) + data = { + 'form-TOTAL_FORMS': '3', + 'form-INITIAL_FORMS': '2', + 'form-MAX_NUM_FORMS': '', + 'form-0-id': str(yellow.pk), + 'form-0-tag': 'hunts', + 'form-1-id': str(hairy.pk), + 'form-1-tag': 'roars', + } + formset = GenericFormSet(data, instance=lion, prefix='form', save_as_new=True) + self.assertTrue(formset.is_valid()) + tags = formset.save() + self.assertEqual([tag.tag for tag in tags], ['hunts', 'roars']) + hunts, roars = tags + self.assertSequenceEqual(lion.tags.order_by('tag'), [hairy, hunts, roars, yellow])