from django import forms from django.contrib.contenttypes.forms import generic_inlineformset_factory from django.contrib.contenttypes.models import ContentType from django.db import models from django.test import TestCase from django.test.utils import isolate_apps from .models import ( Animal, ForProxyModelModel, Gecko, Mineral, ProxyRelatedModel, TaggedItem, ) class CustomWidget(forms.TextInput): pass class TaggedItemForm(forms.ModelForm): class Meta: model = TaggedItem fields = "__all__" widgets = {"tag": CustomWidget} class GenericInlineFormsetTests(TestCase): def test_output(self): GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1) formset = GenericFormSet() self.assertHTMLEqual( "".join(form.as_p() for form in formset.forms), """

""", ) formset = GenericFormSet(instance=Animal()) self.assertHTMLEqual( "".join(form.as_p() for form in formset.forms), """

""", ) platypus = Animal.objects.create( common_name="Platypus", latin_name="Ornithorhynchus anatinus", ) platypus.tags.create(tag="shiny") GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1) formset = GenericFormSet(instance=platypus) tagged_item_id = TaggedItem.objects.get(tag="shiny", object_id=platypus.id).id self.assertHTMLEqual( "".join(form.as_p() for form in formset.forms), """

""" % tagged_item_id, ) lion = Animal.objects.create(common_name="Lion", latin_name="Panthera leo") formset = GenericFormSet(instance=lion, prefix="x") self.assertHTMLEqual( "".join(form.as_p() for form in formset.forms), """

""", ) def test_options(self): TaggedItemFormSet = generic_inlineformset_factory( TaggedItem, can_delete=False, exclude=["tag"], extra=3, ) platypus = Animal.objects.create( common_name="Platypus", latin_name="Ornithorhynchus anatinus" ) harmless = platypus.tags.create(tag="harmless") mammal = platypus.tags.create(tag="mammal") # Works without a queryset. formset = TaggedItemFormSet(instance=platypus) self.assertEqual(len(formset.forms), 5) self.assertHTMLEqual( formset.forms[0].as_p(), '' % harmless.pk, ) self.assertEqual(formset.forms[0].instance, harmless) self.assertEqual(formset.forms[1].instance, mammal) self.assertIsNone(formset.forms[2].instance.pk) # A queryset can be used to alter display ordering. formset = TaggedItemFormSet( instance=platypus, queryset=TaggedItem.objects.order_by("-tag") ) self.assertEqual(len(formset.forms), 5) self.assertEqual(formset.forms[0].instance, mammal) self.assertEqual(formset.forms[1].instance, harmless) self.assertIsNone(formset.forms[2].instance.pk) # A queryset that omits items. formset = TaggedItemFormSet( instance=platypus, queryset=TaggedItem.objects.filter(tag__startswith="harm"), ) self.assertEqual(len(formset.forms), 4) self.assertEqual(formset.forms[0].instance, harmless) self.assertIsNone(formset.forms[1].instance.pk) def test_get_queryset_ordering(self): """ BaseGenericInlineFormSet.get_queryset() adds default ordering, if needed. """ inline_formset = generic_inlineformset_factory(TaggedItem, exclude=("tag",)) formset = inline_formset(instance=Gecko.objects.create()) self.assertIs(formset.get_queryset().ordered, True) def test_initial(self): quartz = Mineral.objects.create(name="Quartz", hardness=7) GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1) ctype = ContentType.objects.get_for_model(quartz) initial_data = [ { "tag": "lizard", "content_type": ctype.pk, "object_id": quartz.pk, } ] formset = GenericFormSet(initial=initial_data) self.assertEqual(formset.forms[0].initial, initial_data[0]) def test_meta_widgets(self): """TaggedItemForm has a widget defined in Meta.""" Formset = generic_inlineformset_factory(TaggedItem, TaggedItemForm) form = Formset().forms[0] self.assertIsInstance(form["tag"].field.widget, CustomWidget) @isolate_apps("generic_relations") def test_incorrect_content_type(self): class BadModel(models.Model): content_type = models.PositiveIntegerField() msg = ( "fk_name 'generic_relations.BadModel.content_type' is not a ForeignKey to " "ContentType" ) with self.assertRaisesMessage(Exception, msg): generic_inlineformset_factory(BadModel, TaggedItemForm) def test_save_new_uses_form_save(self): class SaveTestForm(forms.ModelForm): def save(self, *args, **kwargs): self.instance.saved_by = "custom method" return super().save(*args, **kwargs) Formset = generic_inlineformset_factory( ForProxyModelModel, fields="__all__", form=SaveTestForm ) instance = ProxyRelatedModel.objects.create() data = { "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "0", "form-MAX_NUM_FORMS": "", "form-0-title": "foo", } formset = Formset(data, instance=instance, prefix="form") self.assertTrue(formset.is_valid()) new_obj = formset.save()[0] self.assertEqual(new_obj.saved_by, "custom method") def test_save_new_for_proxy(self): Formset = generic_inlineformset_factory( ForProxyModelModel, fields="__all__", for_concrete_model=False ) instance = ProxyRelatedModel.objects.create() data = { "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "0", "form-MAX_NUM_FORMS": "", "form-0-title": "foo", } formset = Formset(data, instance=instance, prefix="form") self.assertTrue(formset.is_valid()) (new_obj,) = formset.save() self.assertEqual(new_obj.obj, instance) def test_save_new_for_concrete(self): Formset = generic_inlineformset_factory( ForProxyModelModel, fields="__all__", for_concrete_model=True ) instance = ProxyRelatedModel.objects.create() data = { "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "0", "form-MAX_NUM_FORMS": "", "form-0-title": "foo", } formset = Formset(data, instance=instance, prefix="form") 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] ) def test_absolute_max(self): GenericFormSet = generic_inlineformset_factory(TaggedItem, absolute_max=1500) data = { "form-TOTAL_FORMS": "1501", "form-INITIAL_FORMS": "0", "form-MAX_NUM_FORMS": "0", } formset = GenericFormSet(data=data, prefix="form") self.assertIs(formset.is_valid(), False) self.assertEqual(len(formset.forms), 1500) self.assertEqual( formset.non_form_errors(), ["Please submit at most 1000 forms."], ) def test_absolute_max_with_max_num(self): GenericFormSet = generic_inlineformset_factory( TaggedItem, max_num=20, absolute_max=100, ) data = { "form-TOTAL_FORMS": "101", "form-INITIAL_FORMS": "0", "form-MAX_NUM_FORMS": "0", } formset = GenericFormSet(data=data, prefix="form") self.assertIs(formset.is_valid(), False) self.assertEqual(len(formset.forms), 100) self.assertEqual( formset.non_form_errors(), ["Please submit at most 20 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)