From 85890041732dad5ba633af2908e574a70ee2f824 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 12 Feb 2019 18:01:14 -0400 Subject: [PATCH] Add support for single or multiple objects modes View that use the MultipleObjectMixin can now fully operate as single object or multiple object views. Add the self.view_mode_single and self.view_mode_multiple flags. Add support for single, singular and plural titles and success messages via: success_message_single, success_message_singular, sucess_message_plural, title_single, title_singular and title_plural class attributes. Insert object_list and object as attributes of the view class to avoid calling the queryset again. Signed-off-by: Roberto Rosario --- mayan/apps/common/generics.py | 56 +++++++++++++++---------------- mayan/apps/common/mixins.py | 62 ++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 39 deletions(-) diff --git a/mayan/apps/common/generics.py b/mayan/apps/common/generics.py index 014dbe7bda..0506517621 100644 --- a/mayan/apps/common/generics.py +++ b/mayan/apps/common/generics.py @@ -251,32 +251,6 @@ class MultipleObjectDownloadView(RestrictedQuerysetMixin, MultipleObjectMixin, D return super(MultipleObjectDownloadView, self).get_queryset() -class SingleObjectDownloadView(RestrictedQuerysetMixin, SingleObjectMixin, DownloadViewBase): - """ - View that provides a .get_object() method to download content from a - single object. - """ - def __init__(self, *args, **kwargs): - result = super(SingleObjectDownloadView, self).__init__(*args, **kwargs) - - if self.__class__.mro()[0].get_queryset != SingleObjectDownloadView.get_queryset: - raise ImproperlyConfigured( - '%(cls)s is overloading the get_queryset method. Subclasses ' - 'should implement the get_source_queryset method instead. ' % { - 'cls': self.__class__.__name__ - } - ) - - return result - - def get_queryset(self): - try: - return super(SingleObjectDownloadView, self).get_queryset() - except ImproperlyConfigured: - self.queryset = self.get_source_queryset() - return super(SingleObjectDownloadView, self).get_queryset() - - class MultiFormView(DjangoFormView): prefix = None prefixes = {} @@ -607,7 +581,7 @@ class AddRemoveView(ExternalObjectMixin, ExtraContextMixin, ViewPermissionCheckM ) -class MultipleObjectFormActionView(ObjectActionMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, MultipleObjectMixin, FormExtraKwargsMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): +class MultipleObjectFormActionView(ExtraContextMixin, ObjectActionMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, MultipleObjectMixin, FormExtraKwargsMixin, RedirectionMixin, DjangoFormView): """ This view will present a form and upon receiving a POST request will perform an action on an object or queryset @@ -639,7 +613,7 @@ class MultipleObjectFormActionView(ObjectActionMixin, ViewPermissionCheckMixin, return super(MultipleObjectFormActionView, self).get_queryset() -class MultipleObjectConfirmActionView(ObjectActionMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, MultipleObjectMixin, ExtraContextMixin, RedirectionMixin, TemplateView): +class MultipleObjectConfirmActionView(ExtraContextMixin, ObjectActionMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, MultipleObjectMixin, RedirectionMixin, TemplateView): template_name = 'appearance/generic_confirm.html' def __init__(self, *args, **kwargs): @@ -738,6 +712,32 @@ class SingleObjectCreateView(ObjectNameMixin, ViewPermissionCheckMixin, ExtraCon return self.error_message_duplicate +class SingleObjectDownloadView(RestrictedQuerysetMixin, SingleObjectMixin, DownloadViewBase): + """ + View that provides a .get_object() method to download content from a + single object. + """ + def __init__(self, *args, **kwargs): + result = super(SingleObjectDownloadView, self).__init__(*args, **kwargs) + + if self.__class__.mro()[0].get_queryset != SingleObjectDownloadView.get_queryset: + raise ImproperlyConfigured( + '%(cls)s is overloading the get_queryset method. Subclasses ' + 'should implement the get_source_queryset method instead. ' % { + 'cls': self.__class__.__name__ + } + ) + + return result + + def get_queryset(self): + try: + return super(SingleObjectDownloadView, self).get_queryset() + except ImproperlyConfigured: + self.queryset = self.get_source_queryset() + return super(SingleObjectDownloadView, self).get_queryset() + + class SingleObjectDynamicFormCreateView(DynamicFormViewMixin, SingleObjectCreateView): pass diff --git a/mayan/apps/common/mixins.py b/mayan/apps/common/mixins.py index 4e761bcd8e..6e278a68f4 100644 --- a/mayan/apps/common/mixins.py +++ b/mayan/apps/common/mixins.py @@ -169,7 +169,7 @@ class MultipleInstanceActionMixin(object): # MultipleObjectFormActionView or MultipleObjectConfirmActionView model = None - success_message = _('Operation performed on %(count)d object') + success_message_singular = _('Operation performed on %(count)d object') success_message_plural = _('Operation performed on %(count)d objects') def get_pk_list(self): @@ -217,6 +217,13 @@ class MultipleObjectMixin(SingleObjectMixin): pk_list_key = 'id_list' pk_list_separator = PK_LIST_SEPARATOR + def dispatch(self, request, *args, **kwargs): + self.object_list = self.get_object_list() + if self.view_mode_single: + self.object = self.object_list.first() + + return super(MultipleObjectMixin, self).dispatch(request=request, *args, **kwargs) + def get(self, request, *args, **kwargs): """ Override BaseDetailView.get() @@ -243,6 +250,9 @@ class MultipleObjectMixin(SingleObjectMixin): `pk_list' argument in the URLconf, but subclasses can override this to return any object. """ + self.view_mode_single = False + self.view_mode_multiple = False + # Use a custom queryset if provided; this is required for subclasses # like DateDetailView if queryset is None: @@ -255,14 +265,17 @@ class MultipleObjectMixin(SingleObjectMixin): if pk is not None: queryset = queryset.filter(pk=pk) + self.view_mode_single = True # 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}) + self.view_mode_single = True if pk_list is not None: queryset = queryset.filter(pk__in=self.get_pk_list()) + self.view_mode_multiple = True # If none of those are defined, it's an error. if pk is None and slug is None and pk_list is None: @@ -305,22 +318,49 @@ class ObjectActionMixin(object): """ Mixin that performs an user action to a queryset """ - error_message = 'Unable to perform operation on object %(instance)s.' + error_message = 'Unable to perform operation on object %(instance)s; %(exception)s.' post_object_action_url = None - success_message = 'Operation performed on %(count)d object.' + success_message_single = 'Operation performed on %(object)s.' + success_message_singular = 'Operation performed on %(count)d object.' success_message_plural = 'Operation performed on %(count)d objects.' + title_single = 'Perform operation on %(object)s.' + title_singular = 'Perform operation on %(count)d object.' + title_plural = 'Perform operation on %(count)d objects.' + + def get_context_data(self, **kwargs): + context = super(ObjectActionMixin, self).get_context_data(**kwargs) + title = None + + if self.view_mode_single: + title = self.title_single % {'object': self.object} + elif self.view_mode_multiple: + title = ungettext( + singular=self.title_singular, + plural=self.title_plural, + number=self.object_list.count() + ) % { + 'count': self.object_list.count(), + } + + context['title'] = title + + return context def get_post_object_action_url(self): return self.post_object_action_url def get_success_message(self, count): - return ungettext( - singular=self.success_message, - plural=self.success_message_plural, - number=count - ) % { - 'count': count, - } + if self.view_mode_single: + return self.success_message_single % {'object': self.object} + + if self.view_mode_multiple: + return ungettext( + singular=self.success_message_singular, + plural=self.success_message_plural, + number=count + ) % { + 'count': count, + } def object_action(self, instance, form=None): # User supplied method @@ -330,7 +370,7 @@ class ObjectActionMixin(object): self.action_count = 0 self.action_id_list = [] - for instance in self.get_object_list(): + for instance in self.object_list: try: self.object_action(form=form, instance=instance) except Exception as exception: