Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Imports
3from django.http import HttpResponseRedirect
4from django.utils.decorators import method_decorator
5from django.utils.translation import ugettext_lazy as _
6from django.views.decorators.csrf import csrf_exempt
7from superdjango.exceptions import IMustBeMissingSomething
8from .base import BaseView
9from .messages import MessageMixin
11# Exports
13__all__ = (
14 "ConfirmView",
15 "CSRFExemptMixin",
16 "FormMixin",
17 "FormView",
18)
20# Mixins
23class CSRFExemptMixin(object):
24 """Provides `CSRF`_ exemption for a view.
26 .. _CSRF: https://docs.djangoproject.com/en/stable/ref/csrf/
28 .. note::
29 When used, this mixin should be the left-most class of the view.
31 """
33 @method_decorator(csrf_exempt)
34 def dispatch(self, request, *args, **kwargs):
35 """Dispatch the request without CSRF protection."""
36 # noinspection PyUnresolvedReferences
37 return super().dispatch(request, *args, **kwargs)
40class FormMixin(MessageMixin):
41 """Provides form-related methods and attributes."""
43 cancel_text = None
44 """The text to display as part of the cancel URL."""
46 cancel_url = None
47 """The URL to which the user should be sent when canceling the confirmation."""
49 error_message = None
50 """The message to be displayed for an invalid (failed) form submission."""
52 form_class = None
53 """The class to use for rendering forms."""
55 submit_text = None
56 """The text to display for the submit button."""
58 success_message = None
59 """The message to be displayed after a valid (successful) form submission."""
61 success_url = None
62 """The URL to which a user is sent after a successful (valid) form submission."""
64 # noinspection PyUnusedLocal
65 def form_valid(self, form):
66 """Process a valid form and redirect to success URL."""
68 message = self.get_success_message()
69 if message is not None:
70 self.messages.success(message)
72 return HttpResponseRedirect(self.get_success_url())
74 def form_invalid(self, form):
75 """Handle invalid form submission."""
76 # noinspection PyUnresolvedReferences
77 context = self.get_context_data(form=form)
79 message = self.get_error_message()
80 if message is not None:
81 self.messages.error(message)
83 # noinspection PyUnresolvedReferences
84 return self.render_to_response(context)
86 # noinspection PyUnusedLocal
87 def get(self, request, *args, **kwargs):
88 """Handle GET requests."""
89 form = self.get_form()
91 # noinspection PyUnresolvedReferences
92 context = self.get_context_data(form=form)
94 # noinspection PyUnresolvedReferences
95 return self.render_to_response(context)
97 def get_cancel_text(self):
98 """Get the text to display for the cancel link or button.
100 :rtype: str
102 """
103 if self.cancel_text is not None:
104 return self.cancel_text
106 return _("Cancel")
108 def get_cancel_url(self):
109 """Get the URL for canceling the form.
111 :rtype: str | None
113 """
114 return self.cancel_url
116 def get_context_data(self, **kwargs):
117 """Add form-specific values to the context.
119 - ``cancel_text``
120 - ``cancel_url``
121 - ``form``
122 - ``submit_text``
124 """
126 # noinspection PyUnresolvedReferences
127 context = super().get_context_data(**kwargs)
129 context['cancel_text'] = self.get_cancel_text()
130 context['cancel_url'] = self.get_cancel_url()
131 context['submit_text'] = self.get_submit_text()
133 return context
135 def get_error_message(self):
136 """Get the error message for an invalid form submission.
138 :rtype: str | None
140 """
141 return self.error_message
143 def get_form_class(self):
144 """Get the form class used by this view."""
145 if self.form_class is not None:
146 return self.form_class
148 raise IMustBeMissingSomething(self.__class__.__name__, "form_class", "get_form_class")
150 def get_form(self, data=None, files=None, **kwargs):
151 """Get the form instance.
153 :param data: The form data.
154 :type data: QueryDict
156 :param files: Files data.
157 :type files: QueryDict
159 :returns: The instantiated form instance.
161 Any additional keyword arguments are passed directly to the form.
163 """
164 cls = self.get_form_class()
165 return cls(data=data, files=files, **kwargs)
167 def get_submit_text(self):
168 """Get the text to display for the submit button.
170 :rtype: str
172 """
173 if self.submit_text is not None:
174 return self.submit_text
176 return _("Submit")
178 def get_success_message(self):
179 """Get the message to be displayed after a successful form submision.
181 :rtype: str | None
183 """
184 return self.success_message
186 def get_success_url(self):
187 """Get the URL to which the user should be sent on successful form submit.
189 :rtype: str
191 """
192 if self.success_url is not None:
193 return self.success_url
195 # noinspection PyUnresolvedReferences
196 if 'next' in self.request.GET:
197 # noinspection PyUnresolvedReferences
198 return self.request.GET.get("next")
200 raise IMustBeMissingSomething(self.__class__.__name__, "success_url", "get_success_url")
202 # noinspection PyUnusedLocal
203 def post(self, request, *args, **kwargs):
204 """Handle POST requests, i.e form submission."""
205 form = self.get_form(data=request.POST, files=request.FILES)
207 if form.is_valid():
208 return self.form_valid(form)
210 return self.form_invalid(form)
212# Views
215class ConfirmView(FormMixin, BaseView):
216 """Provide a simple confirmation page before proceeding with another action.
218 Note that ``form_valid()`` and ``form_invalid()`` are ignored, and that no form or form class is used.
220 The template is used to provide the confirmation:
222 .. code-block:: html
224 <form method="post">
225 {% csrf_token %}
227 <p>{%trans "Are you sure you want to do this?" %}</p>
229 {% html "form_submit" %}
230 </form>
232 In order for the view to do something, the ``submit()`` method must be overridden:
234 .. code-block:: python
236 from superdjango.ui.views import ConfirmView
238 class MyConfirmView(ConfirmView):
239 pattern_name = "my_confirm_view"
240 pattern_value = 'my/confirmation/'
242 def submit(self, request):
243 # Do something after the form has been submitted.
245 Use the ``confirmed_keyword`` if you need to post data to the confirmation view prior to actual confirmation.
247 .. code-block:: python
249 from superdjango.ui.views import ConfirmView
251 class MyConfirmView(ConfirmView):
252 confirmed_keyword = "confirmed"
253 pattern_name = "my_confirm_view"
254 pattern_value = 'my/confirmation/'
256 def submit(self, request):
257 # Do something after the form has been submitted.
259 The template would also need to include the confirmation keyword:
261 .. code-block:: html
263 <form method="post">
264 {% csrf_token %}
266 <input type="hidden" name="{{ confirmed_keyword }}" value="1">
268 <p>{%trans "Are you sure you want to do this?" %}</p>
270 {% html "form_submit" %}
271 </form>
273 """
275 confirmed_keyword = "confirmed"
276 """The keyword submitted via POST that may used to confirm the next action."""
278 # noinspection PyUnusedLocal
279 def get(self, request, *args, **kwargs):
280 """Handle GET requests."""
281 # noinspection PyAttributeOutsideInit
282 self.request = request
284 context = self.get_context_data()
285 return self.render_to_response(context)
287 def get_context_data(self, **kwargs):
288 """Adds ``confirmed_keyword``."""
289 context = super().get_context_data(**kwargs)
291 context['confirmed_keyword'] = self.confirmed_keyword
293 return context
295 # noinspection PyUnusedLocal
296 def post(self, request, *args, **kwargs):
297 """Handle POST by calling the ``submit()`` method.
299 The presence of ``confirmed_keyword`` is checked prior to calling ``submit()``. This allows data to be posted to
300 the view *without* the confirmation in order to re-use the view for confirming additional actions.
302 """
303 # noinspection PyAttributeOutsideInit
304 self.request = request
306 confirmed = request.POST.get(self.confirmed_keyword, None)
307 if confirmed:
308 self.submit(request)
309 return HttpResponseRedirect(self.get_success_url())
311 context = self.get_context_data()
313 return self.render_to_response(context)
315 def submit(self, request):
316 """Do whatever is required after the action has been confirmed.
318 :param request: The current request instance.
320 .. tip::
321 ``submit()`` does nothing by default. Override this method if you need the confirmation view to do
322 something after the user has confirmed.
324 """
325 pass
328class FormView(FormMixin, BaseView):
329 """Display and handle form submission."""
330 pass