/
form_views.py
153 lines (127 loc) · 5.4 KB
/
form_views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""
This module contains form views for our models that don't need custom handling.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import FieldDoesNotExist
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import BaseCreateView, BaseUpdateView, ModelFormMixin
if TYPE_CHECKING:
from typing import Any
from django.http import HttpResponse, HttpResponseRedirect
from ..forms.custom_model_form import CustomModelForm
from django.db.models.base import ModelBase
from .media import MediaContextMixin
from .mixins import ModelConfirmationContextMixin, ModelTemplateResponseMixin
# pylint: disable=too-many-ancestors
class CustomModelFormMixin(
PermissionRequiredMixin,
ModelTemplateResponseMixin,
ModelFormMixin,
ModelConfirmationContextMixin,
MediaContextMixin,
):
"""
This mixin handles error messages in form views of subclasses of
:class:`~integreat_cms.cms.forms.custom_model_form.CustomModelForm`
"""
#: The suffix to append to the auto-generated candidate template name.
template_name_suffix: str = "_form"
def get_permission_required(self) -> tuple[str]:
"""
Override this method to override the permission_required attribute.
:return: The permissions that are required for views inheriting from this Mixin
"""
# If the form is submitted via POST, require the change permission
if self.request.method == "POST":
return (f"cms.change_{self.model._meta.model_name}",)
# If the form is just retrieved, require the view permission
return (f"cms.view_{self.model._meta.model_name}",)
@property
def model(self) -> ModelBase:
"""
Return the model class of this form mixin
:return: The corresponding Django model
"""
return self.form_class._meta.model
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
r"""
Returns a dictionary representing the template context
(see :meth:`~django.views.generic.base.ContextMixin.get_context_data`).
:param \**kwargs: The given keyword arguments
:return: The template context
"""
context = super().get_context_data(**kwargs)
context.update({"current_menu_item": f"{self.model._meta.model_name}s"})
return context
def get_form_kwargs(self) -> dict[str, Any]:
"""
Return the keyword arguments for instantiating the form
:return: The form kwargs
"""
kwargs = super().get_form_kwargs()
if self.request.region:
kwargs["additional_instance_attributes"] = {"region": self.request.region}
return kwargs
def get_success_url(self) -> str:
"""
Determine the URL to redirect to when the form is successfully validated
:return: The url to redirect on success
"""
kwargs = {}
try:
# Check whether the model has a slug field
self.model._meta.get_field(self.slug_url_kwarg)
kwargs[self.slug_url_kwarg] = self.object.slug
except FieldDoesNotExist:
# If not, use the primary key field as fallback
kwargs[self.pk_url_kwarg] = self.object.pk
if self.request.region:
kwargs["region_slug"] = self.request.region.slug
return reverse(f"edit_{self.object._meta.model_name}", kwargs=kwargs)
def form_valid(self, form: CustomModelForm) -> HttpResponseRedirect:
"""
Saves the form instance, sets the current object for the view, and redirects to :meth:`get_success_url`.
:param form: The valid form instance
:return: A redirection to the success url
"""
if not form.has_changed():
# Add "no changes" messages
messages.info(self.request, _("No changes made"))
elif self.object:
messages.success(
self.request,
_('{} "{}" was successfully saved').format(
self.object._meta.verbose_name, self.object
),
)
else:
messages.success(
self.request,
_('{} "{}" was successfully created').format(
form.instance._meta.verbose_name, form.instance
),
)
return super().form_valid(form)
def form_invalid(self, form: CustomModelForm) -> HttpResponse:
"""
Renders a response, providing the invalid form as context.
:param form: The invalid form instance
:return: The rendered invalid form
"""
form.add_error_messages(self.request)
return super().form_invalid(form)
class CustomCreateView(CustomModelFormMixin, BaseCreateView):
"""
A view that displays a form for creating a region object, redisplaying the form with validation errors (if
there are any) and saving the object.
"""
class CustomUpdateView(CustomModelFormMixin, BaseUpdateView):
"""
A view that displays a form for editing an existing region object, redisplaying the form with validation errors
(if there are any) and saving changes to the object. This uses a form automatically generated from the object's
model class (unless a form class is manually specified).
"""