Source code for boilerplate.mixins

# -*- coding: utf-8 -*-
from django.core.exceptions import PermissionDenied
from django.contrib import messages
from django.db import IntegrityError, transaction
from django.shortcuts import get_object_or_404
try:
    from django.urls import reverse_lazy
except ImportError:
    from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _


[docs]class NoLoginRequiredMixin(object): """ Mixin for any class view classes that required the current user if not authenticated, redirects the user to the home page or any URL with the *next* parameter. **Example** :: class Register(NoLoginRequiredMixin, FormView): form_class = forms.RegisterForm """ def dispatch(self, request, *args, **kwargs): has_access = request.user.is_authenticated if ( (callable(has_access) and has_access()) or (not callable(has_access) and has_access) ): raise PermissionDenied return super(NoLoginRequiredMixin, self).dispatch( request, *args, **kwargs )
class ActionListMixin(object): """ Mixin for :class:`~django.views.generic.list.ListView` classes that adds to the "context" a variable with a list of actions. **Example** :: class ModelList(ActionListMixin, ListView): action_list = ( _('Add'), 'create', 'primary', 'plus'), _('Export'), 'export', 'success', 'export'), ) model = Model """ def get_action_list(self): """ Return the list of actions to show in the "context". Override this function to personalize the list of actions. For example: based on the user permissions. """ return self.action_list def get_context_data(self, **kwargs): if 'actions' not in kwargs: kwargs['action_list'] = self.get_action_list() return super(ActionListMixin, self).get_context_data(**kwargs) class UserCreateMixin(object): field_user = 'user' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) field_name = self.get_field_user() if field_name in context['form'].fields: context['form'].fields[field_name].initial = self.request.user return context def get_field_user(self): return self.field_user def form_valid(self, form): cur_user = getattr(form.instance, self.get_field_user(), None) if not cur_user: setattr(form.instance, self.get_field_user(), self.request.user) return super(UserCreateMixin, self).form_valid(form)
[docs]class CRUDMessageMixin(object): """ Add a message on successful form submission. """ message_action = None success_message = _( '%(model)s: "%(name)s" has been %(action)s successfully.' ) def form_valid(self, form): response = super(CRUDMessageMixin, self).form_valid(form) success_message = self.get_success_message(form.cleaned_data) if success_message: messages.success(self.request, success_message) return response
[docs] def get_success_message(self, cleaned_data=None): """ Default success message mixin """ return self.success_message % dict( model=self.object.__class__._meta.verbose_name.title(), name=self.object, action=self.message_action, )
def get_success_url(self): _addanother = self.request.POST.get('_addanother', None) _continue = self.request.POST.get('_continue', None) if self.object and _addanother: if self.object.parent: url_name = '{}:{}_{}_add'.format( self.object.parent._meta.app_label.lower(), self.object.parent.__class__.__name__.lower(), self.object.__class__.__name__.lower() ) return reverse_lazy(url_name, args=[self.object.parent.pk, ]) else: url_name = '{}:{}_add'.format( self.object._meta.app_label.lower(), self.object.__class__.__name__.lower() ) return reverse_lazy(url_name) elif self.object and _continue: if self.object.parent: url_name = '{}:{}_{}_change'.format( self.object.parent._meta.app_label.lower(), self.object.parent.__class__.__name__.lower(), self.object.__class__.__name__.lower() ) return reverse_lazy( url_name, args=[self.object.parent.pk, self.object.pk, ] ) else: url_name = '{}:{}_change'.format( self.object._meta.app_label.lower(), self.object.__class__.__name__.lower() ) return reverse_lazy(url_name, args=[self.object.pk, ]) return super(CRUDMessageMixin, self).get_success_url()
[docs]class CreateMessageMixin(CRUDMessageMixin): """ Mixin for :class:`~django.views.generic.edit.CreateView` classes that adds a success message after the action is completed successfully. **Example** :: class ModelSendEmail(CreateMessageMixin, CreateView): model = Model """ message_action = 'created'
[docs]class UpdateMessageMixin(CRUDMessageMixin): """ Mixin for :class:`~django.views.generic.edit.UpdateView` classes that adds a success message after the action is completed successfully. **Example** :: class ModelSendEmail(UpdateMessageMixin, UpdateView): model = Model """ message_action = 'updated'
[docs]class DeleteMessageMixin(CRUDMessageMixin): """ Mixin for :class:`~django.views.generic.edit.DeleteView` classes that adds a success message after the action is completed successfully. **Example** :: class ModelSendEmail(DeleteMessageMixin, DeleteView): model = Model """ message_action = 'deleted' def delete(self, request, *args, **kwargs): response = super(DeleteMessageMixin, self).delete( request, *args, **kwargs ) success_message = self.get_success_message() if success_message: messages.success(self.request, success_message) return response
[docs]class ExtraFormsAndFormsetsMixin(object): """ Mixin for :class:`~django.views.generic.edit.CreateView` or :class:`~django.views.generic.edit.UpdateView` classes that adds extra forms and formsets to any of thoose views. **Example** :: class UserUpdate(ExtraFormsAndFormsetsMixin, UpdateView): model = User extra_form_list = ( ('profile', 'user', forms.ProfileForm), ('credit_card', 'user', forms.ProfileForm), ) formset_list = ( (form.AddressFormSet), (form.PhoneFormSet), ) """ extra_form_list = None formset_list = None
[docs] def get_extra_form_list(self): """ Returns a list of extra forms with the lookup_field, relation_field and form_class to use in this view. """ return self.extra_form_list
[docs] def get_formset_list(self): """ Returns a list of formsets with the formset class to use in this view. """ return self.formset_list
[docs] def get_extra_forms(self, form_list=None): """ Returns a list of each extra forms to be used in this view. """ output = list() if form_list is None: form_list = self.get_extra_form_list() if form_list: for lookup_field, relation_field, extra_form in form_list: output.append( ( relation_field, extra_form(**self.get_extra_form_kwargs(lookup_field)) ) ) return output
[docs] def get_formsets(self, formset_list=None): """ Returns a list of formsets to be used in this view. """ output = list() if formset_list is None: formset_list = self.get_formset_list() if formset_list: for formset in formset_list: output.append(formset(**self.get_formset_kwargs())) return output
[docs] def get_extra_form_kwargs(self, lookup_field): """ Returns the keyword arguments for instantiating each extra form. """ kwargs = { 'prefix': lookup_field, } if self.request.method in ('POST', 'PUT'): kwargs.update({ 'data': self.request.POST, 'files': self.request.FILES, }) if hasattr(self, 'object'): try: kwargs.update({'instance': getattr(self.object, lookup_field)}) except Exception: pass return kwargs
[docs] def get_formset_kwargs(self): """ Returns the keyword arguments for instantiating each formset. """ kwargs = {} if self.request.method in ('POST', 'PUT'): kwargs.update({ 'data': self.request.POST, 'files': self.request.FILES, }) if hasattr(self, 'object'): kwargs.update({'instance': self.object}) return kwargs
[docs] def post(self, request, *args, **kwargs): """ Handles POST requests, instantiating a form instance with the passed POST variables and then checked for validity. """ try: self.object = self.get_object() except Exception: self.object = None form = self.get_form() extra_forms = self.get_extra_forms() formsets = self.get_formsets() if not form.is_valid(): return self.form_invalid(form, extra_forms, formsets) for relation_field, extra_form in extra_forms: if not extra_form.is_valid(): return self.form_invalid(form, extra_forms, formsets) for formset in formsets: if not formset.is_valid(): return self.form_invalid(form, extra_forms, formsets) return self.form_valid(form, extra_forms, formsets)
[docs] def form_valid(self, form, extra_forms=None, formsets=None): """ If the form is valid, redirect to the supplied URL. """ try: with transaction.atomic(): response = super( ExtraFormsAndFormsetsMixin, self ).form_valid(form) if not self.object: raise IntegrityError for relation_field, extra_form in extra_forms: setattr(extra_form.instance, relation_field, self.object) if not extra_form.is_valid(): raise IntegrityError extra_form.save() for formset in formsets: formset.instance = self.object if not formset.is_valid(): raise IntegrityError formset.save() except Exception as e: form.add_error(None, e) return self.form_invalid(form, extra_forms, formsets) return response
[docs] def form_invalid(self, form, extra_forms=None, formsets=None): """ If the form or the extra forms are invalid, re-render the context data with the data-filled form and errors. """ return self.render_to_response( self.get_context_data( form=form, extra_forms=extra_forms, formsets=formsets ) )
[docs] def get_context_data(self, **kwargs): """ Insert the extra forms and formsets into the context dict. """ if 'extra_form_list' not in kwargs: kwargs['extra_form_list'] = list() for relation_field, extra_form in self.get_extra_forms(): kwargs['extra_form_list'].append(extra_form) else: extra_forms = list() for relation_field, extra_form in kwargs['extra_form_list']: extra_forms.append(extra_form) kwargs['extra_form_list'] = extra_forms if 'formset_list' not in kwargs: kwargs['formset_list'] = self.get_formsets() return super( ExtraFormsAndFormsetsMixin, self ).get_context_data(**kwargs)
class ParentMixin(object): """ Mixin for :class:`~django.views.generic.edit.CreateView` or :class:`~django.views.generic.edit.UpdateView` classes that adds a success message after the action is completed successfully. **Example** :: class ProfileUpdate(ParentMixin, UpdateView): model = Profile parent_model = User parent_lookup_arg = 'pk_parent' parent_lookup_field = 'pk' """ parent_model = None parent_lookup_arg = 'pk_parent' parent_lookup_field = 'pk' def get_parent_model(self): return self.parent_model def get_parent_lookup_arg(self): return self.parent_lookup_arg def get_parent_lookup_field(self): return self.parent_lookup_field def get_parent_kwargs(self): kwargs = { self.get_parent_lookup_field(): self.kwargs[ self.get_parent_lookup_arg() ], } return kwargs def get_parent(self): return get_object_or_404( self.get_parent_model(), **self.get_parent_kwargs() ) def get_context_data(self, **kwargs): if 'parent_object' not in kwargs: kwargs['parent_object'] = self.get_parent() return super(ParentMixin, self).get_context_data(**kwargs) class ParentCreateMixin(ParentMixin): parent_relation_field = None def get_parent_relation_field(self): return self.parent_relation_field def form_valid(self, form): setattr( form.instance, self.get_parent_relation_field(), self.get_parent() ) return super(ParentCreateMixin, self).form_valid(form) class ParentCreateExtraMixin(ParentCreateMixin): def form_valid(self, form, extra_forms, formsets): setattr(form.instance, self.get_parent_set_field(), self.get_parent()) return super(ParentCreateExtraMixin, self).form_valid( form, extra_forms, formsets ) class ParentSingleObjectMixin(ParentMixin): parent_relation_field = None def get_parent_relation_field(self): return self.parent_relation_field def get_queryset(self): qs = super(ParentSingleObjectMixin, self).get_queryset() kwargs = { self.get_parent_relation_field(): self.get_parent(), } return qs.filter(**kwargs)