1. from django import forms
    
  2. from django.core.exceptions import ImproperlyConfigured
    
  3. from django.test import SimpleTestCase, TestCase, override_settings
    
  4. from django.test.client import RequestFactory
    
  5. from django.urls import reverse
    
  6. from django.views.generic.base import View
    
  7. from django.views.generic.edit import (
    
  8.     CreateView,
    
  9.     DeleteView,
    
  10.     DeleteViewCustomDeleteWarning,
    
  11.     FormMixin,
    
  12.     ModelFormMixin,
    
  13. )
    
  14. 
    
  15. from . import views
    
  16. from .forms import AuthorForm
    
  17. from .models import Artist, Author
    
  18. 
    
  19. 
    
  20. class FormMixinTests(SimpleTestCase):
    
  21.     request_factory = RequestFactory()
    
  22. 
    
  23.     def test_initial_data(self):
    
  24.         """Test instance independence of initial data dict (see #16138)"""
    
  25.         initial_1 = FormMixin().get_initial()
    
  26.         initial_1["foo"] = "bar"
    
  27.         initial_2 = FormMixin().get_initial()
    
  28.         self.assertNotEqual(initial_1, initial_2)
    
  29. 
    
  30.     def test_get_prefix(self):
    
  31.         """Test prefix can be set (see #18872)"""
    
  32.         test_string = "test"
    
  33. 
    
  34.         get_request = self.request_factory.get("/")
    
  35. 
    
  36.         class TestFormMixin(FormMixin):
    
  37.             request = get_request
    
  38. 
    
  39.         default_kwargs = TestFormMixin().get_form_kwargs()
    
  40.         self.assertIsNone(default_kwargs.get("prefix"))
    
  41. 
    
  42.         set_mixin = TestFormMixin()
    
  43.         set_mixin.prefix = test_string
    
  44.         set_kwargs = set_mixin.get_form_kwargs()
    
  45.         self.assertEqual(test_string, set_kwargs.get("prefix"))
    
  46. 
    
  47.     def test_get_form(self):
    
  48.         class TestFormMixin(FormMixin):
    
  49.             request = self.request_factory.get("/")
    
  50. 
    
  51.         self.assertIsInstance(
    
  52.             TestFormMixin().get_form(forms.Form),
    
  53.             forms.Form,
    
  54.             "get_form() should use provided form class.",
    
  55.         )
    
  56. 
    
  57.         class FormClassTestFormMixin(TestFormMixin):
    
  58.             form_class = forms.Form
    
  59. 
    
  60.         self.assertIsInstance(
    
  61.             FormClassTestFormMixin().get_form(),
    
  62.             forms.Form,
    
  63.             "get_form() should fallback to get_form_class() if none is provided.",
    
  64.         )
    
  65. 
    
  66.     def test_get_context_data(self):
    
  67.         class FormContext(FormMixin):
    
  68.             request = self.request_factory.get("/")
    
  69.             form_class = forms.Form
    
  70. 
    
  71.         self.assertIsInstance(FormContext().get_context_data()["form"], forms.Form)
    
  72. 
    
  73. 
    
  74. @override_settings(ROOT_URLCONF="generic_views.urls")
    
  75. class BasicFormTests(TestCase):
    
  76.     def test_post_data(self):
    
  77.         res = self.client.post("/contact/", {"name": "Me", "message": "Hello"})
    
  78.         self.assertRedirects(res, "/list/authors/")
    
  79. 
    
  80.     def test_late_form_validation(self):
    
  81.         """
    
  82.         A form can be marked invalid in the form_valid() method (#25548).
    
  83.         """
    
  84.         res = self.client.post("/late-validation/", {"name": "Me", "message": "Hello"})
    
  85.         self.assertFalse(res.context["form"].is_valid())
    
  86. 
    
  87. 
    
  88. class ModelFormMixinTests(SimpleTestCase):
    
  89.     def test_get_form(self):
    
  90.         form_class = views.AuthorGetQuerySetFormView().get_form_class()
    
  91.         self.assertEqual(form_class._meta.model, Author)
    
  92. 
    
  93.     def test_get_form_checks_for_object(self):
    
  94.         mixin = ModelFormMixin()
    
  95.         mixin.request = RequestFactory().get("/")
    
  96.         self.assertEqual({"initial": {}, "prefix": None}, mixin.get_form_kwargs())
    
  97. 
    
  98. 
    
  99. @override_settings(ROOT_URLCONF="generic_views.urls")
    
  100. class CreateViewTests(TestCase):
    
  101.     def test_create(self):
    
  102.         res = self.client.get("/edit/authors/create/")
    
  103.         self.assertEqual(res.status_code, 200)
    
  104.         self.assertIsInstance(res.context["form"], forms.ModelForm)
    
  105.         self.assertIsInstance(res.context["view"], View)
    
  106.         self.assertNotIn("object", res.context)
    
  107.         self.assertNotIn("author", res.context)
    
  108.         self.assertTemplateUsed(res, "generic_views/author_form.html")
    
  109. 
    
  110.         res = self.client.post(
    
  111.             "/edit/authors/create/",
    
  112.             {"name": "Randall Munroe", "slug": "randall-munroe"},
    
  113.         )
    
  114.         self.assertEqual(res.status_code, 302)
    
  115.         self.assertRedirects(res, "/list/authors/")
    
  116.         self.assertQuerysetEqual(
    
  117.             Author.objects.values_list("name", flat=True), ["Randall Munroe"]
    
  118.         )
    
  119. 
    
  120.     def test_create_invalid(self):
    
  121.         res = self.client.post(
    
  122.             "/edit/authors/create/", {"name": "A" * 101, "slug": "randall-munroe"}
    
  123.         )
    
  124.         self.assertEqual(res.status_code, 200)
    
  125.         self.assertTemplateUsed(res, "generic_views/author_form.html")
    
  126.         self.assertEqual(len(res.context["form"].errors), 1)
    
  127.         self.assertEqual(Author.objects.count(), 0)
    
  128. 
    
  129.     def test_create_with_object_url(self):
    
  130.         res = self.client.post("/edit/artists/create/", {"name": "Rene Magritte"})
    
  131.         self.assertEqual(res.status_code, 302)
    
  132.         artist = Artist.objects.get(name="Rene Magritte")
    
  133.         self.assertRedirects(res, "/detail/artist/%d/" % artist.pk)
    
  134.         self.assertQuerysetEqual(Artist.objects.all(), [artist])
    
  135. 
    
  136.     def test_create_with_redirect(self):
    
  137.         res = self.client.post(
    
  138.             "/edit/authors/create/redirect/",
    
  139.             {"name": "Randall Munroe", "slug": "randall-munroe"},
    
  140.         )
    
  141.         self.assertEqual(res.status_code, 302)
    
  142.         self.assertRedirects(res, "/edit/authors/create/")
    
  143.         self.assertQuerysetEqual(
    
  144.             Author.objects.values_list("name", flat=True), ["Randall Munroe"]
    
  145.         )
    
  146. 
    
  147.     def test_create_with_interpolated_redirect(self):
    
  148.         res = self.client.post(
    
  149.             "/edit/authors/create/interpolate_redirect/",
    
  150.             {"name": "Randall Munroe", "slug": "randall-munroe"},
    
  151.         )
    
  152.         self.assertQuerysetEqual(
    
  153.             Author.objects.values_list("name", flat=True), ["Randall Munroe"]
    
  154.         )
    
  155.         self.assertEqual(res.status_code, 302)
    
  156.         pk = Author.objects.first().pk
    
  157.         self.assertRedirects(res, "/edit/author/%d/update/" % pk)
    
  158.         # Also test with escaped chars in URL
    
  159.         res = self.client.post(
    
  160.             "/edit/authors/create/interpolate_redirect_nonascii/",
    
  161.             {"name": "John Doe", "slug": "john-doe"},
    
  162.         )
    
  163.         self.assertEqual(res.status_code, 302)
    
  164.         pk = Author.objects.get(name="John Doe").pk
    
  165.         self.assertRedirects(res, "/%C3%A9dit/author/{}/update/".format(pk))
    
  166. 
    
  167.     def test_create_with_special_properties(self):
    
  168.         res = self.client.get("/edit/authors/create/special/")
    
  169.         self.assertEqual(res.status_code, 200)
    
  170.         self.assertIsInstance(res.context["form"], views.AuthorForm)
    
  171.         self.assertNotIn("object", res.context)
    
  172.         self.assertNotIn("author", res.context)
    
  173.         self.assertTemplateUsed(res, "generic_views/form.html")
    
  174. 
    
  175.         res = self.client.post(
    
  176.             "/edit/authors/create/special/",
    
  177.             {"name": "Randall Munroe", "slug": "randall-munroe"},
    
  178.         )
    
  179.         self.assertEqual(res.status_code, 302)
    
  180.         obj = Author.objects.get(slug="randall-munroe")
    
  181.         self.assertRedirects(res, reverse("author_detail", kwargs={"pk": obj.pk}))
    
  182.         self.assertQuerysetEqual(Author.objects.all(), [obj])
    
  183. 
    
  184.     def test_create_without_redirect(self):
    
  185.         msg = (
    
  186.             "No URL to redirect to.  Either provide a url or define a "
    
  187.             "get_absolute_url method on the Model."
    
  188.         )
    
  189.         with self.assertRaisesMessage(ImproperlyConfigured, msg):
    
  190.             self.client.post(
    
  191.                 "/edit/authors/create/naive/",
    
  192.                 {"name": "Randall Munroe", "slug": "randall-munroe"},
    
  193.             )
    
  194. 
    
  195.     def test_create_restricted(self):
    
  196.         res = self.client.post(
    
  197.             "/edit/authors/create/restricted/",
    
  198.             {"name": "Randall Munroe", "slug": "randall-munroe"},
    
  199.         )
    
  200.         self.assertEqual(res.status_code, 302)
    
  201.         self.assertRedirects(
    
  202.             res, "/accounts/login/?next=/edit/authors/create/restricted/"
    
  203.         )
    
  204. 
    
  205.     def test_create_view_with_restricted_fields(self):
    
  206.         class MyCreateView(CreateView):
    
  207.             model = Author
    
  208.             fields = ["name"]
    
  209. 
    
  210.         self.assertEqual(list(MyCreateView().get_form_class().base_fields), ["name"])
    
  211. 
    
  212.     def test_create_view_all_fields(self):
    
  213.         class MyCreateView(CreateView):
    
  214.             model = Author
    
  215.             fields = "__all__"
    
  216. 
    
  217.         self.assertEqual(
    
  218.             list(MyCreateView().get_form_class().base_fields), ["name", "slug"]
    
  219.         )
    
  220. 
    
  221.     def test_create_view_without_explicit_fields(self):
    
  222.         class MyCreateView(CreateView):
    
  223.             model = Author
    
  224. 
    
  225.         message = (
    
  226.             "Using ModelFormMixin (base class of MyCreateView) without the "
    
  227.             "'fields' attribute is prohibited."
    
  228.         )
    
  229.         with self.assertRaisesMessage(ImproperlyConfigured, message):
    
  230.             MyCreateView().get_form_class()
    
  231. 
    
  232.     def test_define_both_fields_and_form_class(self):
    
  233.         class MyCreateView(CreateView):
    
  234.             model = Author
    
  235.             form_class = AuthorForm
    
  236.             fields = ["name"]
    
  237. 
    
  238.         message = "Specifying both 'fields' and 'form_class' is not permitted."
    
  239.         with self.assertRaisesMessage(ImproperlyConfigured, message):
    
  240.             MyCreateView().get_form_class()
    
  241. 
    
  242. 
    
  243. @override_settings(ROOT_URLCONF="generic_views.urls")
    
  244. class UpdateViewTests(TestCase):
    
  245.     @classmethod
    
  246.     def setUpTestData(cls):
    
  247.         cls.author = Author.objects.create(
    
  248.             pk=1,  # Required for OneAuthorUpdate.
    
  249.             name="Randall Munroe",
    
  250.             slug="randall-munroe",
    
  251.         )
    
  252. 
    
  253.     def test_update_post(self):
    
  254.         res = self.client.get("/edit/author/%d/update/" % self.author.pk)
    
  255.         self.assertEqual(res.status_code, 200)
    
  256.         self.assertIsInstance(res.context["form"], forms.ModelForm)
    
  257.         self.assertEqual(res.context["object"], self.author)
    
  258.         self.assertEqual(res.context["author"], self.author)
    
  259.         self.assertTemplateUsed(res, "generic_views/author_form.html")
    
  260.         self.assertEqual(res.context["view"].get_form_called_count, 1)
    
  261. 
    
  262.         # Modification with both POST and PUT (browser compatible)
    
  263.         res = self.client.post(
    
  264.             "/edit/author/%d/update/" % self.author.pk,
    
  265.             {"name": "Randall Munroe (xkcd)", "slug": "randall-munroe"},
    
  266.         )
    
  267.         self.assertEqual(res.status_code, 302)
    
  268.         self.assertRedirects(res, "/list/authors/")
    
  269.         self.assertQuerysetEqual(
    
  270.             Author.objects.values_list("name", flat=True), ["Randall Munroe (xkcd)"]
    
  271.         )
    
  272. 
    
  273.     def test_update_invalid(self):
    
  274.         res = self.client.post(
    
  275.             "/edit/author/%d/update/" % self.author.pk,
    
  276.             {"name": "A" * 101, "slug": "randall-munroe"},
    
  277.         )
    
  278.         self.assertEqual(res.status_code, 200)
    
  279.         self.assertTemplateUsed(res, "generic_views/author_form.html")
    
  280.         self.assertEqual(len(res.context["form"].errors), 1)
    
  281.         self.assertQuerysetEqual(Author.objects.all(), [self.author])
    
  282.         self.assertEqual(res.context["view"].get_form_called_count, 1)
    
  283. 
    
  284.     def test_update_with_object_url(self):
    
  285.         a = Artist.objects.create(name="Rene Magritte")
    
  286.         res = self.client.post(
    
  287.             "/edit/artists/%d/update/" % a.pk, {"name": "Rene Magritte"}
    
  288.         )
    
  289.         self.assertEqual(res.status_code, 302)
    
  290.         self.assertRedirects(res, "/detail/artist/%d/" % a.pk)
    
  291.         self.assertQuerysetEqual(Artist.objects.all(), [a])
    
  292. 
    
  293.     def test_update_with_redirect(self):
    
  294.         res = self.client.post(
    
  295.             "/edit/author/%d/update/redirect/" % self.author.pk,
    
  296.             {"name": "Randall Munroe (author of xkcd)", "slug": "randall-munroe"},
    
  297.         )
    
  298.         self.assertEqual(res.status_code, 302)
    
  299.         self.assertRedirects(res, "/edit/authors/create/")
    
  300.         self.assertQuerysetEqual(
    
  301.             Author.objects.values_list("name", flat=True),
    
  302.             ["Randall Munroe (author of xkcd)"],
    
  303.         )
    
  304. 
    
  305.     def test_update_with_interpolated_redirect(self):
    
  306.         res = self.client.post(
    
  307.             "/edit/author/%d/update/interpolate_redirect/" % self.author.pk,
    
  308.             {"name": "Randall Munroe (author of xkcd)", "slug": "randall-munroe"},
    
  309.         )
    
  310.         self.assertQuerysetEqual(
    
  311.             Author.objects.values_list("name", flat=True),
    
  312.             ["Randall Munroe (author of xkcd)"],
    
  313.         )
    
  314.         self.assertEqual(res.status_code, 302)
    
  315.         pk = Author.objects.first().pk
    
  316.         self.assertRedirects(res, "/edit/author/%d/update/" % pk)
    
  317.         # Also test with escaped chars in URL
    
  318.         res = self.client.post(
    
  319.             "/edit/author/%d/update/interpolate_redirect_nonascii/" % self.author.pk,
    
  320.             {"name": "John Doe", "slug": "john-doe"},
    
  321.         )
    
  322.         self.assertEqual(res.status_code, 302)
    
  323.         pk = Author.objects.get(name="John Doe").pk
    
  324.         self.assertRedirects(res, "/%C3%A9dit/author/{}/update/".format(pk))
    
  325. 
    
  326.     def test_update_with_special_properties(self):
    
  327.         res = self.client.get("/edit/author/%d/update/special/" % self.author.pk)
    
  328.         self.assertEqual(res.status_code, 200)
    
  329.         self.assertIsInstance(res.context["form"], views.AuthorForm)
    
  330.         self.assertEqual(res.context["object"], self.author)
    
  331.         self.assertEqual(res.context["thingy"], self.author)
    
  332.         self.assertNotIn("author", res.context)
    
  333.         self.assertTemplateUsed(res, "generic_views/form.html")
    
  334. 
    
  335.         res = self.client.post(
    
  336.             "/edit/author/%d/update/special/" % self.author.pk,
    
  337.             {"name": "Randall Munroe (author of xkcd)", "slug": "randall-munroe"},
    
  338.         )
    
  339.         self.assertEqual(res.status_code, 302)
    
  340.         self.assertRedirects(res, "/detail/author/%d/" % self.author.pk)
    
  341.         self.assertQuerysetEqual(
    
  342.             Author.objects.values_list("name", flat=True),
    
  343.             ["Randall Munroe (author of xkcd)"],
    
  344.         )
    
  345. 
    
  346.     def test_update_without_redirect(self):
    
  347.         msg = (
    
  348.             "No URL to redirect to.  Either provide a url or define a "
    
  349.             "get_absolute_url method on the Model."
    
  350.         )
    
  351.         with self.assertRaisesMessage(ImproperlyConfigured, msg):
    
  352.             self.client.post(
    
  353.                 "/edit/author/%d/update/naive/" % self.author.pk,
    
  354.                 {"name": "Randall Munroe (author of xkcd)", "slug": "randall-munroe"},
    
  355.             )
    
  356. 
    
  357.     def test_update_get_object(self):
    
  358.         res = self.client.get("/edit/author/update/")
    
  359.         self.assertEqual(res.status_code, 200)
    
  360.         self.assertIsInstance(res.context["form"], forms.ModelForm)
    
  361.         self.assertIsInstance(res.context["view"], View)
    
  362.         self.assertEqual(res.context["object"], self.author)
    
  363.         self.assertEqual(res.context["author"], self.author)
    
  364.         self.assertTemplateUsed(res, "generic_views/author_form.html")
    
  365. 
    
  366.         # Modification with both POST and PUT (browser compatible)
    
  367.         res = self.client.post(
    
  368.             "/edit/author/update/",
    
  369.             {"name": "Randall Munroe (xkcd)", "slug": "randall-munroe"},
    
  370.         )
    
  371.         self.assertEqual(res.status_code, 302)
    
  372.         self.assertRedirects(res, "/list/authors/")
    
  373.         self.assertQuerysetEqual(
    
  374.             Author.objects.values_list("name", flat=True), ["Randall Munroe (xkcd)"]
    
  375.         )
    
  376. 
    
  377. 
    
  378. @override_settings(ROOT_URLCONF="generic_views.urls")
    
  379. class DeleteViewTests(TestCase):
    
  380.     @classmethod
    
  381.     def setUpTestData(cls):
    
  382.         cls.author = Author.objects.create(
    
  383.             name="Randall Munroe",
    
  384.             slug="randall-munroe",
    
  385.         )
    
  386. 
    
  387.     def test_delete_by_post(self):
    
  388.         res = self.client.get("/edit/author/%d/delete/" % self.author.pk)
    
  389.         self.assertEqual(res.status_code, 200)
    
  390.         self.assertEqual(res.context["object"], self.author)
    
  391.         self.assertEqual(res.context["author"], self.author)
    
  392.         self.assertTemplateUsed(res, "generic_views/author_confirm_delete.html")
    
  393. 
    
  394.         # Deletion with POST
    
  395.         res = self.client.post("/edit/author/%d/delete/" % self.author.pk)
    
  396.         self.assertEqual(res.status_code, 302)
    
  397.         self.assertRedirects(res, "/list/authors/")
    
  398.         self.assertQuerysetEqual(Author.objects.all(), [])
    
  399. 
    
  400.     def test_delete_by_delete(self):
    
  401.         # Deletion with browser compatible DELETE method
    
  402.         res = self.client.delete("/edit/author/%d/delete/" % self.author.pk)
    
  403.         self.assertEqual(res.status_code, 302)
    
  404.         self.assertRedirects(res, "/list/authors/")
    
  405.         self.assertQuerysetEqual(Author.objects.all(), [])
    
  406. 
    
  407.     def test_delete_with_redirect(self):
    
  408.         res = self.client.post("/edit/author/%d/delete/redirect/" % self.author.pk)
    
  409.         self.assertEqual(res.status_code, 302)
    
  410.         self.assertRedirects(res, "/edit/authors/create/")
    
  411.         self.assertQuerysetEqual(Author.objects.all(), [])
    
  412. 
    
  413.     def test_delete_with_interpolated_redirect(self):
    
  414.         res = self.client.post(
    
  415.             "/edit/author/%d/delete/interpolate_redirect/" % self.author.pk
    
  416.         )
    
  417.         self.assertEqual(res.status_code, 302)
    
  418.         self.assertRedirects(res, "/edit/authors/create/?deleted=%d" % self.author.pk)
    
  419.         self.assertQuerysetEqual(Author.objects.all(), [])
    
  420.         # Also test with escaped chars in URL
    
  421.         a = Author.objects.create(
    
  422.             **{"name": "Randall Munroe", "slug": "randall-munroe"}
    
  423.         )
    
  424.         res = self.client.post(
    
  425.             "/edit/author/{}/delete/interpolate_redirect_nonascii/".format(a.pk)
    
  426.         )
    
  427.         self.assertEqual(res.status_code, 302)
    
  428.         self.assertRedirects(res, "/%C3%A9dit/authors/create/?deleted={}".format(a.pk))
    
  429. 
    
  430.     def test_delete_with_special_properties(self):
    
  431.         res = self.client.get("/edit/author/%d/delete/special/" % self.author.pk)
    
  432.         self.assertEqual(res.status_code, 200)
    
  433.         self.assertEqual(res.context["object"], self.author)
    
  434.         self.assertEqual(res.context["thingy"], self.author)
    
  435.         self.assertNotIn("author", res.context)
    
  436.         self.assertTemplateUsed(res, "generic_views/confirm_delete.html")
    
  437. 
    
  438.         res = self.client.post("/edit/author/%d/delete/special/" % self.author.pk)
    
  439.         self.assertEqual(res.status_code, 302)
    
  440.         self.assertRedirects(res, "/list/authors/")
    
  441.         self.assertQuerysetEqual(Author.objects.all(), [])
    
  442. 
    
  443.     def test_delete_without_redirect(self):
    
  444.         msg = "No URL to redirect to. Provide a success_url."
    
  445.         with self.assertRaisesMessage(ImproperlyConfigured, msg):
    
  446.             self.client.post("/edit/author/%d/delete/naive/" % self.author.pk)
    
  447. 
    
  448.     def test_delete_with_form_as_post(self):
    
  449.         res = self.client.get("/edit/author/%d/delete/form/" % self.author.pk)
    
  450.         self.assertEqual(res.status_code, 200)
    
  451.         self.assertEqual(res.context["object"], self.author)
    
  452.         self.assertEqual(res.context["author"], self.author)
    
  453.         self.assertTemplateUsed(res, "generic_views/author_confirm_delete.html")
    
  454.         res = self.client.post(
    
  455.             "/edit/author/%d/delete/form/" % self.author.pk, data={"confirm": True}
    
  456.         )
    
  457.         self.assertEqual(res.status_code, 302)
    
  458.         self.assertRedirects(res, "/list/authors/")
    
  459.         self.assertSequenceEqual(Author.objects.all(), [])
    
  460. 
    
  461.     def test_delete_with_form_as_post_with_validation_error(self):
    
  462.         res = self.client.get("/edit/author/%d/delete/form/" % self.author.pk)
    
  463.         self.assertEqual(res.status_code, 200)
    
  464.         self.assertEqual(res.context["object"], self.author)
    
  465.         self.assertEqual(res.context["author"], self.author)
    
  466.         self.assertTemplateUsed(res, "generic_views/author_confirm_delete.html")
    
  467. 
    
  468.         res = self.client.post("/edit/author/%d/delete/form/" % self.author.pk)
    
  469.         self.assertEqual(res.status_code, 200)
    
  470.         self.assertEqual(len(res.context_data["form"].errors), 2)
    
  471.         self.assertEqual(
    
  472.             res.context_data["form"].errors["__all__"],
    
  473.             ["You must confirm the delete."],
    
  474.         )
    
  475.         self.assertEqual(
    
  476.             res.context_data["form"].errors["confirm"],
    
  477.             ["This field is required."],
    
  478.         )
    
  479. 
    
  480.     # RemovedInDjango50Warning.
    
  481.     def test_delete_with_custom_delete(self):
    
  482.         class AuthorDeleteView(DeleteView):
    
  483.             model = Author
    
  484. 
    
  485.             def delete(self, request, *args, **kwargs):
    
  486.                 # Custom logic.
    
  487.                 pass
    
  488. 
    
  489.         msg = (
    
  490.             "DeleteView uses FormMixin to handle POST requests. As a "
    
  491.             "consequence, any custom deletion logic in "
    
  492.             "AuthorDeleteView.delete() handler should be moved to "
    
  493.             "form_valid()."
    
  494.         )
    
  495.         with self.assertWarnsMessage(DeleteViewCustomDeleteWarning, msg):
    
  496.             AuthorDeleteView()