Plain is headed towards 1.0! Subscribe for development updates →

 1from collections.abc import Callable
 2from typing import TYPE_CHECKING
 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:
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 self.get_template().render(context)
52
53    def get_template_context(self) -> dict:
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)