Coverage for src/mesh/views/views_base.py: 33%
46 statements
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
1from typing import Any
3from django.contrib import messages
4from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
5from django.utils import timezone
6from django.utils.translation import gettext_lazy as _
7from ptf.url_utils import add_query_parameters_to_url
9from mesh.views.forms.base_forms import (
10 SUBMIT_QUERY_PARAMETER,
11 FormAction,
12 SubmittableModelForm,
13)
16class SubmittableModelFormMixin:
17 """
18 View mixin for submittable model forms.
19 To be used together with:
20 - RoleMixin
21 - TemplateResponseMixin
22 - FormMixin - The form class must inherit SubmittableModelForm
24 This uses the same view for saving the form (and model), submitting the form
25 and confirming the submission of the form.
26 The status is known through the booleans `_submit` and `_submit_confirm`.
27 The `form_pre_save` and `form_post_save` hooks enable additional processing.
29 Normal workflow:
30 1. The user saves the form any number of times (draft).
31 2. When ready, the user clicks the "Submit" button
32 3. The user is redirected to a recap of the form to be submitted (default: same
33 route & view with the disabled form).
34 4. The user confirms the submission of the form.
35 5. The underlying model gets updated: `submitted=True` & `date_submitted=now`
37 Steps 4 and 5 have a simple implementation in this view mixin.
38 These steps can be externalized to a distinct view. In that case, don't forget
39 to update the model's `submitted` and `date_submitted` fields !
40 """
42 # Whether the request (POST) is the submission of the form/model.
43 _submit = False
44 # Whether the request (GET or POST) is the confirmation of the submission
45 # of the form/model.
46 _submit_confirm = False
47 # Whether to add a message when redirecting to the confirmation URL.
48 add_confirm_message = True
49 # Only for typing. This attribute should be set somewhere else (usually by the
50 # RoleMixin)
51 request: HttpRequest
53 def submit_url(self) -> str:
54 """
55 URL to redirect to to when the user submits the form.
57 The default is the current URL with an added query parameter indicating
58 the confirmation request.
59 """
60 return add_query_parameters_to_url(
61 self.request.build_absolute_uri(), {SUBMIT_QUERY_PARAMETER: ["true"]}
62 )
64 def form_pre_save(self, form: SubmittableModelForm) -> None:
65 """
66 Hook called before `form_valid` (wich saves the underlying model to DB)
67 when posting the form.
68 """
69 pass
71 def form_post_save(
72 self, form: SubmittableModelForm, original_response: HttpResponse
73 ) -> HttpResponse:
74 """
75 Hook called after `form_valid` (wich saves the underlying model to DB)
76 when posting the form.
78 This must return an HTTP response.
79 """
80 return original_response
82 def form_valid(self, form: SubmittableModelForm):
83 """
84 Checks whether the POST request is a simple save, a submit or
85 a submit confirmation.
86 """
87 if FormAction.SUBMIT.value in self.request.POST:
88 self._submit = True
89 elif (
90 FormAction.SUBMIT_CONFIRM.value in self.request.POST
91 and form.instance.submitted is False
92 ):
93 self._submit_confirm = True
94 form.instance.submitted = True
95 if form.instance.date_submitted is None:
96 form.instance.date_submitted = timezone.now()
98 self.form_pre_save(form)
100 resp = super().form_valid(form) # type:ignore
102 if self._submit:
103 resp = HttpResponseRedirect(self.submit_url())
104 if self.add_confirm_message:
105 messages.info(self.request, _("Please confirm the submission of the form."))
107 resp = self.form_post_save(form, resp)
109 return resp
111 def get_context_data(self, *args, **kwargs):
112 context = super().get_context_data(*args, **kwargs) # type:ignore
113 context["submit_confirm"] = self._submit_confirm
114 return context
116 def get(self, request, *args, **kwargs):
117 """
118 Overloads the default get to catch whether it's a submit confirmation
119 request.
120 """
121 self._submit_confirm = self.request.GET.get(SUBMIT_QUERY_PARAMETER, None) == "true"
122 return super().get(request, *args, **kwargs) # type:ignore
124 def get_form_kwargs(self) -> dict[str, Any]:
125 kwargs = super().get_form_kwargs() # type:ignore
126 if self.request.GET.get(SUBMIT_QUERY_PARAMETER, None) == "true":
127 kwargs[SUBMIT_QUERY_PARAMETER] = True
128 return kwargs