diff --git a/mayan/apps/common/views.py b/mayan/apps/common/views.py index abb516a816..db03ad9570 100644 --- a/mayan/apps/common/views.py +++ b/mayan/apps/common/views.py @@ -7,7 +7,7 @@ from django.contrib import messages from django.contrib.auth.models import User from django.contrib.auth.views import login, password_change from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import PermissionDenied +from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect from django.shortcuts import redirect, render_to_response @@ -15,6 +15,7 @@ from django.template import RequestContext from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _ from django.views.generic import FormView, TemplateView +from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.list import ListView @@ -32,6 +33,7 @@ from .mixins import ( ExtraContextMixin, ObjectListPermissionFilterMixin, ObjectPermissionCheckMixin, RedirectionMixin, ViewPermissionCheckMixin ) +from .utils import return_attrib class AssignRemoveView(TemplateView): @@ -370,6 +372,84 @@ class SingleObjectListView(ViewPermissionCheckMixin, ObjectListPermissionFilterM template_name = 'appearance/generic_list.html' +class ParentChildListView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, ListView, SingleObjectMixin): + parent_model = None + parent_queryset = None + template_name = 'appearance/generic_list.html' + + def get(self, request, *args, **kwargs): + # Parent + self.object = self.get_object() + + # Children + self.object_list = self.get_queryset() + allow_empty = self.get_allow_empty() + if not allow_empty: + # When pagination is enabled and object_list is a queryset, + # it's better to do a cheap query than to load the unpaginated + # queryset in memory. + if (self.get_paginate_by(self.object_list) is not None + and hasattr(self.object_list, 'exists')): + is_empty = not self.object_list.exists() + else: + is_empty = len(self.object_list) == 0 + if is_empty: + raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") + % {'class_name': self.__class__.__name__}) + + context = self.get_context_data(object=self.object) + return self.render_to_response(context) + + def get_object(self, queryset=None): + # Use a custom queryset if provided; this is required for subclasses + # like DateDetailView + if queryset is None: + queryset = self.get_parent_queryset() + # Next, try looking up by primary key. + pk = self.kwargs.get(self.pk_url_kwarg, None) + slug = self.kwargs.get(self.slug_url_kwarg, None) + if pk is not None: + queryset = queryset.filter(pk=pk) + # Next, try looking up by slug. + if slug is not None and (pk is None or self.query_pk_and_slug): + slug_field = self.get_slug_field() + queryset = queryset.filter(**{slug_field: slug}) + # If none of those are defined, it's an error. + if pk is None and slug is None: + raise AttributeError("Generic detail view %s must be called with " + "either an object pk or a slug." + % self.__class__.__name__) + try: + # Get the single item from the filtered queryset + obj = queryset.get() + except queryset.model.DoesNotExist: + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) + return obj + + def get_parent_queryset(self): + """ + Return the `QuerySet` that will be used to look up the object. + Note that this method is called by the default implementation of + `get_object` and may not be called if `get_object` is overridden. + """ + if self.parent_queryset is None: + if self.parent_model: + return self.model._default_manager.all() + else: + raise ImproperlyConfigured( + "%(cls)s is missing a QuerySet. Define " + "%(cls)s.parent_model, %(cls)s.parent_queryset, or override " + "%(cls)s.get_parent_queryset()." % { + 'cls': self.__class__.__name__ + } + ) + return self.parent_queryset.all() + + def get_queryset(self): + raise NotImplementedError + + class SetupListView(TemplateView): template_name = 'appearance/generic_list_horizontal.html'