1. from django.forms.models import ModelForm, inlineformset_factory
    
  2. from django.test import TestCase, skipUnlessDBFeature
    
  3. 
    
  4. from .models import Child, Parent, Poem, Poet, School
    
  5. 
    
  6. 
    
  7. class DeletionTests(TestCase):
    
  8.     def test_deletion(self):
    
  9.         PoemFormSet = inlineformset_factory(
    
  10.             Poet, Poem, can_delete=True, fields="__all__"
    
  11.         )
    
  12.         poet = Poet.objects.create(name="test")
    
  13.         poem = poet.poem_set.create(name="test poem")
    
  14.         data = {
    
  15.             "poem_set-TOTAL_FORMS": "1",
    
  16.             "poem_set-INITIAL_FORMS": "1",
    
  17.             "poem_set-MAX_NUM_FORMS": "0",
    
  18.             "poem_set-0-id": str(poem.pk),
    
  19.             "poem_set-0-poet": str(poet.pk),
    
  20.             "poem_set-0-name": "test",
    
  21.             "poem_set-0-DELETE": "on",
    
  22.         }
    
  23.         formset = PoemFormSet(data, instance=poet)
    
  24.         formset.save()
    
  25.         self.assertTrue(formset.is_valid())
    
  26.         self.assertEqual(Poem.objects.count(), 0)
    
  27. 
    
  28.     def test_add_form_deletion_when_invalid(self):
    
  29.         """
    
  30.         Make sure that an add form that is filled out, but marked for deletion
    
  31.         doesn't cause validation errors.
    
  32.         """
    
  33.         PoemFormSet = inlineformset_factory(
    
  34.             Poet, Poem, can_delete=True, fields="__all__"
    
  35.         )
    
  36.         poet = Poet.objects.create(name="test")
    
  37.         data = {
    
  38.             "poem_set-TOTAL_FORMS": "1",
    
  39.             "poem_set-INITIAL_FORMS": "0",
    
  40.             "poem_set-MAX_NUM_FORMS": "0",
    
  41.             "poem_set-0-id": "",
    
  42.             "poem_set-0-poem": "1",
    
  43.             "poem_set-0-name": "x" * 1000,
    
  44.         }
    
  45.         formset = PoemFormSet(data, instance=poet)
    
  46.         # Make sure this form doesn't pass validation.
    
  47.         self.assertIs(formset.is_valid(), False)
    
  48.         self.assertEqual(Poem.objects.count(), 0)
    
  49. 
    
  50.         # Then make sure that it *does* pass validation and delete the object,
    
  51.         # even though the data isn't actually valid.
    
  52.         data["poem_set-0-DELETE"] = "on"
    
  53.         formset = PoemFormSet(data, instance=poet)
    
  54.         self.assertIs(formset.is_valid(), True)
    
  55.         formset.save()
    
  56.         self.assertEqual(Poem.objects.count(), 0)
    
  57. 
    
  58.     def test_change_form_deletion_when_invalid(self):
    
  59.         """
    
  60.         Make sure that a change form that is filled out, but marked for deletion
    
  61.         doesn't cause validation errors.
    
  62.         """
    
  63.         PoemFormSet = inlineformset_factory(
    
  64.             Poet, Poem, can_delete=True, fields="__all__"
    
  65.         )
    
  66.         poet = Poet.objects.create(name="test")
    
  67.         poem = poet.poem_set.create(name="test poem")
    
  68.         data = {
    
  69.             "poem_set-TOTAL_FORMS": "1",
    
  70.             "poem_set-INITIAL_FORMS": "1",
    
  71.             "poem_set-MAX_NUM_FORMS": "0",
    
  72.             "poem_set-0-id": str(poem.id),
    
  73.             "poem_set-0-poem": str(poem.id),
    
  74.             "poem_set-0-name": "x" * 1000,
    
  75.         }
    
  76.         formset = PoemFormSet(data, instance=poet)
    
  77.         # Make sure this form doesn't pass validation.
    
  78.         self.assertIs(formset.is_valid(), False)
    
  79.         self.assertEqual(Poem.objects.count(), 1)
    
  80. 
    
  81.         # Then make sure that it *does* pass validation and delete the object,
    
  82.         # even though the data isn't actually valid.
    
  83.         data["poem_set-0-DELETE"] = "on"
    
  84.         formset = PoemFormSet(data, instance=poet)
    
  85.         self.assertIs(formset.is_valid(), True)
    
  86.         formset.save()
    
  87.         self.assertEqual(Poem.objects.count(), 0)
    
  88. 
    
  89.     def test_save_new(self):
    
  90.         """
    
  91.         Make sure inlineformsets respect commit=False
    
  92.         regression for #10750
    
  93.         """
    
  94.         # exclude some required field from the forms
    
  95.         ChildFormSet = inlineformset_factory(
    
  96.             School, Child, exclude=["father", "mother"]
    
  97.         )
    
  98.         school = School.objects.create(name="test")
    
  99.         mother = Parent.objects.create(name="mother")
    
  100.         father = Parent.objects.create(name="father")
    
  101.         data = {
    
  102.             "child_set-TOTAL_FORMS": "1",
    
  103.             "child_set-INITIAL_FORMS": "0",
    
  104.             "child_set-MAX_NUM_FORMS": "0",
    
  105.             "child_set-0-name": "child",
    
  106.         }
    
  107.         formset = ChildFormSet(data, instance=school)
    
  108.         self.assertIs(formset.is_valid(), True)
    
  109.         objects = formset.save(commit=False)
    
  110.         for obj in objects:
    
  111.             obj.mother = mother
    
  112.             obj.father = father
    
  113.             obj.save()
    
  114.         self.assertEqual(school.child_set.count(), 1)
    
  115. 
    
  116. 
    
  117. class InlineFormsetFactoryTest(TestCase):
    
  118.     def test_inline_formset_factory(self):
    
  119.         """
    
  120.         These should both work without a problem.
    
  121.         """
    
  122.         inlineformset_factory(Parent, Child, fk_name="mother", fields="__all__")
    
  123.         inlineformset_factory(Parent, Child, fk_name="father", fields="__all__")
    
  124. 
    
  125.     def test_exception_on_unspecified_foreign_key(self):
    
  126.         """
    
  127.         Child has two ForeignKeys to Parent, so if we don't specify which one
    
  128.         to use for the inline formset, we should get an exception.
    
  129.         """
    
  130.         msg = (
    
  131.             "'inline_formsets.Child' has more than one ForeignKey to "
    
  132.             "'inline_formsets.Parent'."
    
  133.         )
    
  134.         with self.assertRaisesMessage(ValueError, msg):
    
  135.             inlineformset_factory(Parent, Child)
    
  136. 
    
  137.     def test_fk_name_not_foreign_key_field_from_child(self):
    
  138.         """
    
  139.         If we specify fk_name, but it isn't a ForeignKey from the child model
    
  140.         to the parent model, we should get an exception.
    
  141.         """
    
  142.         msg = "fk_name 'school' is not a ForeignKey to 'inline_formsets.Parent'."
    
  143.         with self.assertRaisesMessage(ValueError, msg):
    
  144.             inlineformset_factory(Parent, Child, fk_name="school")
    
  145. 
    
  146.     def test_non_foreign_key_field(self):
    
  147.         """
    
  148.         If the field specified in fk_name is not a ForeignKey, we should get an
    
  149.         exception.
    
  150.         """
    
  151.         with self.assertRaisesMessage(
    
  152.             ValueError, "'inline_formsets.Child' has no field named 'test'."
    
  153.         ):
    
  154.             inlineformset_factory(Parent, Child, fk_name="test")
    
  155. 
    
  156.     def test_any_iterable_allowed_as_argument_to_exclude(self):
    
  157.         # Regression test for #9171.
    
  158.         inlineformset_factory(Parent, Child, exclude=["school"], fk_name="mother")
    
  159. 
    
  160.         inlineformset_factory(Parent, Child, exclude=("school",), fk_name="mother")
    
  161. 
    
  162.     @skipUnlessDBFeature("allows_auto_pk_0")
    
  163.     def test_zero_primary_key(self):
    
  164.         # Regression test for #21472
    
  165.         poet = Poet.objects.create(id=0, name="test")
    
  166.         poet.poem_set.create(name="test poem")
    
  167.         PoemFormSet = inlineformset_factory(Poet, Poem, fields="__all__", extra=0)
    
  168.         formset = PoemFormSet(None, instance=poet)
    
  169.         self.assertEqual(len(formset.forms), 1)
    
  170. 
    
  171.     def test_unsaved_fk_validate_unique(self):
    
  172.         poet = Poet(name="unsaved")
    
  173.         PoemFormSet = inlineformset_factory(Poet, Poem, fields=["name"])
    
  174.         data = {
    
  175.             "poem_set-TOTAL_FORMS": "2",
    
  176.             "poem_set-INITIAL_FORMS": "0",
    
  177.             "poem_set-MAX_NUM_FORMS": "2",
    
  178.             "poem_set-0-name": "Poem",
    
  179.             "poem_set-1-name": "Poem",
    
  180.         }
    
  181.         formset = PoemFormSet(data, instance=poet)
    
  182.         self.assertFalse(formset.is_valid())
    
  183.         self.assertEqual(
    
  184.             formset.non_form_errors(), ["Please correct the duplicate data for name."]
    
  185.         )
    
  186. 
    
  187.     def test_fk_not_duplicated_in_form_fields(self):
    
  188.         """
    
  189.         A foreign key name isn't duplicated in form._meta fields (#21332).
    
  190.         """
    
  191.         poet = Poet.objects.create(name="test")
    
  192.         poet.poem_set.create(name="first test poem")
    
  193.         poet.poem_set.create(name="second test poem")
    
  194.         poet.poem_set.create(name="third test poem")
    
  195.         PoemFormSet = inlineformset_factory(Poet, Poem, fields=("name",), extra=0)
    
  196.         formset = PoemFormSet(None, instance=poet)
    
  197.         self.assertEqual(len(formset.forms), 3)
    
  198.         self.assertEqual(["name", "poet"], PoemFormSet.form._meta.fields)
    
  199. 
    
  200.     def test_fk_in_all_formset_forms(self):
    
  201.         """
    
  202.         A foreign key field is in Meta for all forms in the formset (#26538).
    
  203.         """
    
  204. 
    
  205.         class PoemModelForm(ModelForm):
    
  206.             def __init__(self, *args, **kwargs):
    
  207.                 assert "poet" in self._meta.fields
    
  208.                 super().__init__(*args, **kwargs)
    
  209. 
    
  210.         poet = Poet.objects.create(name="test")
    
  211.         poet.poem_set.create(name="first test poem")
    
  212.         poet.poem_set.create(name="second test poem")
    
  213.         PoemFormSet = inlineformset_factory(
    
  214.             Poet, Poem, form=PoemModelForm, fields=("name",), extra=0
    
  215.         )
    
  216.         formset = PoemFormSet(None, instance=poet)
    
  217.         formset.forms  # Trigger form instantiation to run the assert above.