From 6411433b243957d7547136611753f24258952e05 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sun, 14 Apr 2019 16:53:58 -0400 Subject: [PATCH] Add FilteredSelectionForm class Use FilteredSelectionForm to reduce boilerplate code for TagMultipleSelectionForm. Signed-off-by: Roberto Rosario --- HISTORY.rst | 2 + docs/releases/3.2.rst | 2 + mayan/apps/common/forms.py | 72 ++++++++++++++++++- mayan/apps/tags/forms.py | 38 +++------- .../tags/forms/widgets/tag_select_option.html | 2 - mayan/apps/tags/views.py | 2 + mayan/apps/tags/widgets.py | 10 ++- mayan/apps/tags/wizard_steps.py | 4 ++ mayan/apps/tags/workflow_actions.py | 2 - 9 files changed, 94 insertions(+), 40 deletions(-) delete mode 100644 mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html diff --git a/HISTORY.rst b/HISTORY.rst index 1d7ad3e24e..feb8efa0d5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -55,6 +55,8 @@ * Reduce form boilerplate code with new FormOptions class. * Use FormOptions for the DetailForm class. * DetailForm now support help text on extra fields. +* Add FilteredSelectionForm class. +* Use FilteredSelectionForm for TagMultipleSelectionForm. 3.1.11 (2019-04-XX) =================== diff --git a/docs/releases/3.2.rst b/docs/releases/3.2.rst index 8df3657f49..8c4a8365c6 100644 --- a/docs/releases/3.2.rst +++ b/docs/releases/3.2.rst @@ -79,6 +79,8 @@ Other changes * Reduce form boilerplate code with new FormOptions class. * Use FormOptions for the DetailForm class. * DetailForm now support help text on extra fields. +* Add FilteredSelectionForm class. +* Use FilteredSelectionForm for TagMultipleSelectionForm. Removals -------- diff --git a/mayan/apps/common/forms.py b/mayan/apps/common/forms.py index 5fcaaacc5a..9a8f3e58a6 100644 --- a/mayan/apps/common/forms.py +++ b/mayan/apps/common/forms.py @@ -6,11 +6,13 @@ from django import forms from django.conf import settings from django.contrib.admin.utils import label_for_field from django.contrib.auth import get_user_model -from django.core.exceptions import FieldDoesNotExist +from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured from django.db import models from django.utils.module_loading import import_string from django.utils.translation import ugettext_lazy as _ +from mayan.apps.acls.models import AccessControlList + from .classes import Package from .models import UserLocaleProfile from .utils import introspect_attribute, resolve_attribute @@ -198,6 +200,74 @@ class FileDisplayForm(forms.Form): self.fields['text'].initial = file_object.read() +class FilteredSelectionFormOptions(FormOptions): + # Dictionary list of option names and default values + option_definitions = { + 'allow_multiple': False, + 'field_name': None, + 'help_text': None, + 'label': None, + 'model': None, + 'permission': None, + 'queryset': None, + 'required': True, + 'user': None, + 'widget_class': None, + 'widget_attributes': {'size': '10'}, + } + + +class FilteredSelectionForm(forms.Form): + """ + Form to select the from a list of choice filtered by access. Can be + configure to allow single or multiple selection. + """ + def __init__(self, *args, **kwargs): + opts = FilteredSelectionFormOptions( + form=self, kwargs=kwargs, options=getattr(self, 'Meta', None) + ) + + if opts.queryset is None: + if not opts.model: + raise ImproperlyConfigured( + '{} requires a queryset or a model to be specified as ' + 'a meta option or passed during initialization.'.format( + self.__class__.__name__ + ) + ) + + queryset = opts.model.objects.all() + else: + queryset = opts.queryset + + if opts.allow_multiple: + extra_kwargs = {} + field_class = forms.ModelMultipleChoiceField + widget_class = forms.widgets.SelectMultiple + else: + extra_kwargs = {'empty_label': None} + field_class = forms.ModelChoiceField + widget_class = forms.widgets.Select + + if opts.widget_class: + widget_class = opts.widget_class + + if opts.permission: + queryset = AccessControlList.objects.filter_by_access( + permission=opts.permission, queryset=queryset, + user=opts.user + ) + + super(FilteredSelectionForm, self).__init__(*args, **kwargs) + + self.fields[opts.field_name] = field_class( + help_text=opts.help_text, label=opts.label, + queryset=queryset, required=opts.required, + widget=widget_class(attrs=opts.widget_attributes), + **extra_kwargs + ) + + class LicenseForm(FileDisplayForm): DIRECTORY = () FILENAME = 'LICENSE' diff --git a/mayan/apps/tags/forms.py b/mayan/apps/tags/forms.py index 7da1d6d848..8cdbd34b9e 100644 --- a/mayan/apps/tags/forms.py +++ b/mayan/apps/tags/forms.py @@ -1,40 +1,20 @@ from __future__ import absolute_import, unicode_literals -import logging - -from django import forms from django.utils.translation import ugettext_lazy as _ -from mayan.apps.acls.models import AccessControlList +from mayan.apps.common.forms import FilteredSelectionForm -from .models import Tag -from .permissions import permission_tag_view from .widgets import TagFormWidget -logger = logging.getLogger(__name__) - -class TagMultipleSelectionForm(forms.Form): +class TagMultipleSelectionForm(FilteredSelectionForm): class Media: js = ('tags/js/tags_form.js',) - def __init__(self, *args, **kwargs): - help_text = kwargs.pop('help_text', None) - permission = kwargs.pop('permission', permission_tag_view) - queryset = kwargs.pop('queryset', Tag.objects.all()) - user = kwargs.pop('user', None) - - logger.debug('user: %s', user) - super(TagMultipleSelectionForm, self).__init__(*args, **kwargs) - - queryset = AccessControlList.objects.filter_by_access( - permission=permission, queryset=queryset, user=user - ) - - self.fields['tags'] = forms.ModelMultipleChoiceField( - label=_('Tags'), help_text=help_text, - queryset=queryset, required=False, - widget=TagFormWidget( - attrs={'class': 'select2-tags'}, queryset=queryset - ) - ) + class Meta: + allow_multiple = True + field_name = 'tags' + label = _('Tags') + required = False + widget_class = TagFormWidget + widget_attributes = {'class': 'select2-tags'} diff --git a/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html b/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html deleted file mode 100644 index 8dabad9809..0000000000 --- a/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html +++ /dev/null @@ -1,2 +0,0 @@ -{% include 'django/forms/widgets/select_option.html' %} - diff --git a/mayan/apps/tags/views.py b/mayan/apps/tags/views.py index 1f740d4a13..cc545f9a46 100644 --- a/mayan/apps/tags/views.py +++ b/mayan/apps/tags/views.py @@ -69,6 +69,7 @@ class TagAttachActionView(MultipleObjectFormActionView): result = { 'help_text': _('Tags to be attached.'), 'permission': permission_tag_attach, + 'queryset': Tag.objects.all(), 'user': self.request.user } @@ -311,6 +312,7 @@ class TagRemoveActionView(MultipleObjectFormActionView): result = { 'help_text': _('Tags to be removed.'), 'permission': permission_tag_remove, + 'queryset': Tag.objects.all(), 'user': self.request.user } diff --git a/mayan/apps/tags/widgets.py b/mayan/apps/tags/widgets.py index d532d0e8c1..d008fe888e 100644 --- a/mayan/apps/tags/widgets.py +++ b/mayan/apps/tags/widgets.py @@ -10,19 +10,17 @@ from .permissions import permission_tag_view class TagFormWidget(forms.SelectMultiple): - option_template_name = 'tags/forms/widgets/tag_select_option.html' - def __init__(self, *args, **kwargs): - self.queryset = kwargs.pop('queryset') return super(TagFormWidget, self).__init__(*args, **kwargs) def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): result = super(TagFormWidget, self).create_option( - name=name, value=value, label='{}'.format(conditional_escape(label)), - selected=selected, index=index, subindex=subindex, attrs=attrs + attrs=attrs, index=index, + label='{}'.format(conditional_escape(label)), name=name, + selected=selected, subindex=subindex, value=value ) - result['attrs']['data-color'] = self.queryset.get(pk=value).color + result['attrs']['data-color'] = self.choices.queryset.get(pk=value).color return result diff --git a/mayan/apps/tags/wizard_steps.py b/mayan/apps/tags/wizard_steps.py index 0f87893e10..9961b5b1a0 100644 --- a/mayan/apps/tags/wizard_steps.py +++ b/mayan/apps/tags/wizard_steps.py @@ -9,6 +9,8 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.sources.wizards import WizardStep from .forms import TagMultipleSelectionForm +from .models import Tag +from .permissions import permission_tag_attach class WizardStepTags(WizardStep): @@ -26,6 +28,8 @@ class WizardStepTags(WizardStep): def get_form_kwargs(self, wizard): return { 'help_text': _('Tags to be attached.'), + 'model': Tag, + 'permission': permission_tag_attach, 'user': wizard.request.user } diff --git a/mayan/apps/tags/workflow_actions.py b/mayan/apps/tags/workflow_actions.py index d7b626c219..25df860648 100644 --- a/mayan/apps/tags/workflow_actions.py +++ b/mayan/apps/tags/workflow_actions.py @@ -28,7 +28,6 @@ class AttachTagAction(WorkflowAction): 'tags': { 'class': 'tags.widgets.TagFormWidget', 'kwargs': { 'attrs': {'class': 'select2-tags'}, - 'queryset': Tag.objects.none() } } } @@ -43,7 +42,6 @@ class AttachTagAction(WorkflowAction): ) self.fields['tags']['kwargs']['queryset'] = queryset - self.widgets['tags']['kwargs']['queryset'] = queryset return { 'fields': self.fields,