diff --git a/django/contrib/contenttypes/forms.py b/django/contrib/contenttypes/forms.py index ce1c361b6c..92a58d49f8 100644 --- a/django/contrib/contenttypes/forms.py +++ b/django/contrib/contenttypes/forms.py @@ -57,7 +57,7 @@ def generic_inlineformset_factory(model, form=ModelForm, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True, min_num=None, validate_min=False, - absolute_max=None): + absolute_max=None, can_delete_extra=True): """ Return a ``GenericInlineFormSet`` for the given kwargs. @@ -76,7 +76,7 @@ def generic_inlineformset_factory(model, form=ModelForm, formset=formset, extra=extra, can_delete=can_delete, can_order=can_order, fields=fields, exclude=exclude, max_num=max_num, validate_max=validate_max, min_num=min_num, validate_min=validate_min, - absolute_max=absolute_max, + absolute_max=absolute_max, can_delete_extra=can_delete_extra, ) FormSet.ct_field = ct_field FormSet.ct_fk_field = fk_field diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt index 6cc0033132..28a2e40819 100644 --- a/docs/ref/contrib/contenttypes.txt +++ b/docs/ref/contrib/contenttypes.txt @@ -466,7 +466,7 @@ The :mod:`django.contrib.contenttypes.forms` module provides: .. class:: BaseGenericInlineFormSet -.. function:: generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field="content_type", fk_field="object_id", fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True, min_num=None, validate_min=False, absolute_max=None) +.. function:: generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field="content_type", fk_field="object_id", fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True, min_num=None, validate_min=False, absolute_max=None, can_delete_extra=True) Returns a ``GenericInlineFormSet`` using :func:`~django.forms.models.modelformset_factory`. @@ -483,7 +483,7 @@ The :mod:`django.contrib.contenttypes.forms` module provides: .. versionchanged:: 3.2 - The ``absolute_max`` argument was added. + The ``absolute_max`` and ``can_delete_extra`` arguments were added. .. module:: django.contrib.contenttypes.admin diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt index 1acea66cff..3a55edb591 100644 --- a/docs/releases/3.2.txt +++ b/docs/releases/3.2.txt @@ -90,6 +90,11 @@ Minor features allows customizing the maximum number of forms that can be instantiated when supplying ``POST`` data. See :ref:`formsets-absolute-max` for more details. +* The new ``can_delete_extra`` argument for + :func:`~django.contrib.contenttypes.forms.generic_inlineformset_factory` + allows removal of the option to delete extra forms. See + :attr:`~.BaseFormSet.can_delete_extra` for more information. + :mod:`django.contrib.gis` ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/generic_relations/test_forms.py b/tests/generic_relations/test_forms.py index 1a05681711..f5e76fd3f5 100644 --- a/tests/generic_relations/test_forms.py +++ b/tests/generic_relations/test_forms.py @@ -271,3 +271,27 @@ id="id_generic_relations-taggeditem-content_type-object_id-1-id">

""" % tagge formset.non_form_errors(), ['Please submit 20 or fewer forms.'], ) + + def test_can_delete_extra(self): + GenericFormSet = generic_inlineformset_factory( + TaggedItem, + can_delete=True, + can_delete_extra=True, + extra=2, + ) + formset = GenericFormSet() + self.assertEqual(len(formset), 2) + self.assertIn('DELETE', formset.forms[0].fields) + self.assertIn('DELETE', formset.forms[1].fields) + + def test_disable_delete_extra(self): + GenericFormSet = generic_inlineformset_factory( + TaggedItem, + can_delete=True, + can_delete_extra=False, + extra=2, + ) + formset = GenericFormSet() + self.assertEqual(len(formset), 2) + self.assertNotIn('DELETE', formset.forms[0].fields) + self.assertNotIn('DELETE', formset.forms[1].fields)