1from collections.abc import Callable
2from typing import TYPE_CHECKING, Any
3
4from plain.exceptions import ImproperlyConfigured
5from plain.http import Response, ResponseRedirect
6
7from .templates import TemplateView
8
9if TYPE_CHECKING:
10 from plain.forms import BaseForm
11
12
13class FormView(TemplateView):
14 """A view for displaying a form and rendering a template response."""
15
16 form_class: type["BaseForm"] | None = None
17 success_url: Callable | str | None = None
18
19 def get_form(self) -> "BaseForm":
20 """Return an instance of the form to be used in this view."""
21 if not self.form_class:
22 raise ImproperlyConfigured(
23 f"No form class provided. Define {self.__class__.__name__}.form_class or override "
24 f"{self.__class__.__name__}.get_form()."
25 )
26 return self.form_class(**self.get_form_kwargs())
27
28 def get_form_kwargs(self) -> dict[str, Any]:
29 """Return the keyword arguments for instantiating the form."""
30 return {
31 "initial": {},
32 "request": self.request,
33 }
34
35 def get_success_url(self, form: "BaseForm") -> str:
36 """Return the URL to redirect to after processing a valid form."""
37 if not self.success_url:
38 raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
39 return str(self.success_url) # success_url may be lazy
40
41 def form_valid(self, form: "BaseForm") -> Response:
42 """If the form is valid, redirect to the supplied URL."""
43 return ResponseRedirect(self.get_success_url(form))
44
45 def form_invalid(self, form: "BaseForm") -> Response:
46 """If the form is invalid, render the invalid form."""
47 context = {
48 **self.get_template_context(),
49 "form": form,
50 }
51 return Response(self.get_template().render(context))
52
53 def get_template_context(self) -> dict[str, Any]:
54 """Insert the form into the context dict."""
55 context = super().get_template_context()
56 context["form"] = self.get_form()
57 return context
58
59 def post(self) -> Response:
60 """
61 Handle POST requests: instantiate a form instance with the passed
62 POST variables and then check if it's valid.
63 """
64 form = self.get_form()
65 if form.is_valid():
66 return self.form_valid(form)
67 else:
68 return self.form_invalid(form)