Source code for superdjango.views.forms

# Imports

from django.http import HttpResponseRedirect
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from superdjango.exceptions import IMustBeMissingSomething
from .base import BaseView
from .messages import MessageMixin

# Exports

__all__ = (
    "ConfirmView",
    "CSRFExemptMixin",
    "FormMixin",
    "FormView",
)

# Mixins


[docs]class CSRFExemptMixin(object): """Provides `CSRF`_ exemption for a view. .. _CSRF: https://docs.djangoproject.com/en/stable/ref/csrf/ .. note:: When used, this mixin should be the left-most class of the view. """
[docs] @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): """Dispatch the request without CSRF protection.""" # noinspection PyUnresolvedReferences return super().dispatch(request, *args, **kwargs)
[docs]class FormMixin(MessageMixin): """Provides form-related methods and attributes.""" cancel_text = None """The text to display as part of the cancel URL.""" cancel_url = None """The URL to which the user should be sent when canceling the confirmation.""" error_message = None """The message to be displayed for an invalid (failed) form submission.""" form_class = None """The class to use for rendering forms.""" submit_text = None """The text to display for the submit button.""" success_message = None """The message to be displayed after a valid (successful) form submission.""" success_url = None """The URL to which a user is sent after a successful (valid) form submission.""" # noinspection PyUnusedLocal
[docs] def form_valid(self, form): """Process a valid form and redirect to success URL.""" message = self.get_success_message() if message is not None: self.messages.success(message) return HttpResponseRedirect(self.get_success_url())
[docs] def form_invalid(self, form): """Handle invalid form submission.""" # noinspection PyUnresolvedReferences context = self.get_context_data(form=form) message = self.get_error_message() if message is not None: self.messages.error(message) # noinspection PyUnresolvedReferences return self.render_to_response(context)
# noinspection PyUnusedLocal
[docs] def get(self, request, *args, **kwargs): """Handle GET requests.""" form = self.get_form() # noinspection PyUnresolvedReferences context = self.get_context_data(form=form) # noinspection PyUnresolvedReferences return self.render_to_response(context)
[docs] def get_cancel_text(self): """Get the text to display for the cancel link or button. :rtype: str """ if self.cancel_text is not None: return self.cancel_text return _("Cancel")
[docs] def get_cancel_url(self): """Get the URL for canceling the form. :rtype: str | None """ return self.cancel_url
[docs] def get_context_data(self, **kwargs): """Add form-specific values to the context. - ``cancel_text`` - ``cancel_url`` - ``form`` - ``submit_text`` """ # noinspection PyUnresolvedReferences context = super().get_context_data(**kwargs) context['cancel_text'] = self.get_cancel_text() context['cancel_url'] = self.get_cancel_url() context['submit_text'] = self.get_submit_text() return context
[docs] def get_error_message(self): """Get the error message for an invalid form submission. :rtype: str | None """ return self.error_message
[docs] def get_form_class(self): """Get the form class used by this view.""" if self.form_class is not None: return self.form_class raise IMustBeMissingSomething(self.__class__.__name__, "form_class", "get_form_class")
[docs] def get_form(self, data=None, files=None, **kwargs): """Get the form instance. :param data: The form data. :type data: QueryDict :param files: Files data. :type files: QueryDict :returns: The instantiated form instance. Any additional keyword arguments are passed directly to the form. """ cls = self.get_form_class() return cls(data=data, files=files, **kwargs)
[docs] def get_submit_text(self): """Get the text to display for the submit button. :rtype: str """ if self.submit_text is not None: return self.submit_text return _("Submit")
[docs] def get_success_message(self): """Get the message to be displayed after a successful form submision. :rtype: str | None """ return self.success_message
[docs] def get_success_url(self): """Get the URL to which the user should be sent on successful form submit. :rtype: str """ if self.success_url is not None: return self.success_url # noinspection PyUnresolvedReferences if 'next' in self.request.GET: # noinspection PyUnresolvedReferences return self.request.GET.get("next") raise IMustBeMissingSomething(self.__class__.__name__, "success_url", "get_success_url")
# noinspection PyUnusedLocal
[docs] def post(self, request, *args, **kwargs): """Handle POST requests, i.e form submission.""" form = self.get_form(data=request.POST, files=request.FILES) if form.is_valid(): return self.form_valid(form) return self.form_invalid(form)
# Views
[docs]class ConfirmView(FormMixin, BaseView): """Provide a simple confirmation page before proceeding with another action. Note that ``form_valid()`` and ``form_invalid()`` are ignored, and that no form or form class is used. The template is used to provide the confirmation: .. code-block:: html <form method="post"> {% csrf_token %} <p>{%trans "Are you sure you want to do this?" %}</p> {% html "form_submit" %} </form> In order for the view to do something, the ``submit()`` method must be overridden: .. code-block:: python from superdjango.ui.views import ConfirmView class MyConfirmView(ConfirmView): pattern_name = "my_confirm_view" pattern_value = 'my/confirmation/' def submit(self, request): # Do something after the form has been submitted. Use the ``confirmed_keyword`` if you need to post data to the confirmation view prior to actual confirmation. .. code-block:: python from superdjango.ui.views import ConfirmView class MyConfirmView(ConfirmView): confirmed_keyword = "confirmed" pattern_name = "my_confirm_view" pattern_value = 'my/confirmation/' def submit(self, request): # Do something after the form has been submitted. The template would also need to include the confirmation keyword: .. code-block:: html <form method="post"> {% csrf_token %} <input type="hidden" name="{{ confirmed_keyword }}" value="1"> <p>{%trans "Are you sure you want to do this?" %}</p> {% html "form_submit" %} </form> """ confirmed_keyword = "confirmed" """The keyword submitted via POST that may used to confirm the next action.""" # noinspection PyUnusedLocal
[docs] def get(self, request, *args, **kwargs): """Handle GET requests.""" # noinspection PyAttributeOutsideInit self.request = request context = self.get_context_data() return self.render_to_response(context)
[docs] def get_context_data(self, **kwargs): """Adds ``confirmed_keyword``.""" context = super().get_context_data(**kwargs) context['confirmed_keyword'] = self.confirmed_keyword return context
# noinspection PyUnusedLocal
[docs] def post(self, request, *args, **kwargs): """Handle POST by calling the ``submit()`` method. The presence of ``confirmed_keyword`` is checked prior to calling ``submit()``. This allows data to be posted to the view *without* the confirmation in order to re-use the view for confirming additional actions. """ # noinspection PyAttributeOutsideInit self.request = request confirmed = request.POST.get(self.confirmed_keyword, None) if confirmed: self.submit(request) return HttpResponseRedirect(self.get_success_url()) context = self.get_context_data() return self.render_to_response(context)
[docs] def submit(self, request): """Do whatever is required after the action has been confirmed. :param request: The current request instance. .. tip:: ``submit()`` does nothing by default. Override this method if you need the confirmation view to do something after the user has confirmed. """ pass
[docs]class FormView(FormMixin, BaseView): """Display and handle form submission.""" pass