From e002f317a40008d1a6b6c56e7c73ddb694ae0412 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Wed, 21 Aug 2019 03:52:07 -0400 Subject: [PATCH] Add NamedMultiWidget class Works as Django's MultiWidget but instead of a list of subwidgets, uses a dictionary of subwidgets with names as the keys. The named nature is less error prone than Django's index numbered subwidgets. Signed-off-by: Roberto Rosario --- mayan/apps/common/widgets.py | 82 ++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/mayan/apps/common/widgets.py b/mayan/apps/common/widgets.py index 38fdd8e8e7..30ac5cb223 100644 --- a/mayan/apps/common/widgets.py +++ b/mayan/apps/common/widgets.py @@ -1,5 +1,7 @@ from __future__ import unicode_literals +from collections import OrderedDict + from django import forms from django.utils.safestring import mark_safe @@ -20,6 +22,86 @@ class DisableableSelectWidget(forms.widgets.SelectMultiple): return result +class NamedMultiWidget(forms.widgets.Widget): + template_name = 'django/forms/widgets/multiwidget.html' + + def __init__(self, widgets, attrs=None): + self.widgets = OrderedDict() + for name, widget in widgets.items(): + self.widgets[name] = widget() if isinstance(widget, type) else widget + + super(NamedMultiWidget, self).__init__(attrs) + + def _get_media(self): + "Media for a multiwidget is the combination of all media of the subwidgets" + media = forms.widgets.Media() + for name, widget in self.widgets.items(): + media = media + widget.media + return media + media = property(_get_media) + + @property + def is_hidden(self): + return all(widget.is_hidden for name, widget in self.widgets.items()) + + def get_context(self, name, value, attrs): + context = super(NamedMultiWidget, self).get_context(name, value, attrs) + if self.is_localized: + for widget in self.widgets: + widget.is_localized = self.is_localized + + value = self.decompress(value) + + final_attrs = context['widget']['attrs'] + input_type = final_attrs.pop('type', None) + id_ = final_attrs.get('id') + subwidgets = [] + for widget_name, widget in self.widgets.items(): + if input_type is not None: + widget.input_type = input_type + full_widget_name = '%s_%s' % (name, widget_name) + try: + widget_value = value[widget_name] + except IndexError: + widget_value = None + if id_: + widget_attrs = final_attrs.copy() + widget_attrs['id'] = '%s_%s' % (id_, widget_name) + else: + widget_attrs = final_attrs + subwidgets.append( + widget.get_context( + full_widget_name, widget_value, widget_attrs + )['widget'] + ) + context['widget']['subwidgets'] = subwidgets + return context + + def id_for_label(self, id_): + if id_: + id_ += '_{}'.format(self.widgets.keys()[0]) + return id_ + + def value_from_datadict(self, data, files, name): + return { + name: widget.value_from_datadict( + data, files, name + '_%s' % name + ) for name, widget in self.widgets.items() + } + + def value_omitted_from_data(self, data, files, name): + return all( + widget.value_omitted_from_data(data, files, name + '_%s' % name) + for name, widget in self.widgets.items() + ) + + @property + def needs_multipart_form(self): + return any( + widget.needs_multipart_form for name, widget in self.widgets.items() + ) + + class PlainWidget(forms.widgets.Widget): """ Class to define a form widget that effectively nulls the htmls of a