From 46815b6e34ec269366b36597c3caa8274e3bec3f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 3 Feb 2009 18:36:06 +0100 Subject: [PATCH] Make the DropDownMultiple widget degrade properly. DropDownMultiple now extends the original SelectMultiple widget and always renders both widgets. The DropDownMultiple widget is disabled and hidden by default in the HTML and is enabled by Javascript code, keeping things working even when Javascript is not available. As an added advantage, we can now reuse some of the code from SelectMultiple. In particular, by using the render_options method, we should now also support more complicated (like grouped) lists of choices. --- tools/widgets/dropdownmultiple.py | 59 ++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/tools/widgets/dropdownmultiple.py b/tools/widgets/dropdownmultiple.py index 37e17e4..5bbe27e 100644 --- a/tools/widgets/dropdownmultiple.py +++ b/tools/widgets/dropdownmultiple.py @@ -1,26 +1,27 @@ +# Code taken from http://www.djangosnippets.org/snippets/747/ # -*- coding: utf-8 -*- from django.forms import widgets from django.utils.safestring import mark_safe from django.utils.datastructures import MultiValueDict -from django.newforms.util import flatatt +from django.forms.util import flatatt TPL_OPTION = """""" TPL_SELECT = """ - %(opts)s """ TPL_SCRIPT = """ """ TPL_FULL = """ - + """ -class DropDownMultiple(widgets.Widget): - choices = None - +class DropDownMultiple(widgets.SelectMultiple): def __init__(self, attrs=None, choices=()): - self.choices = choices + super(DropDownMultiple, self).__init__(attrs=attrs, choices=choices) + + def render(self, name, value, attrs=None, choices=()): + # Always render both the default SelectMultiple and our + # javascript assisted version. This allows for graceful + # degradation when javascript is not available or enabled + # (together with the javascript code above). + nonjs_output = super(DropDownMultiple, self).render(name, value, attrs=attrs, choices=choices) + js_output = self.render_js(name, value, attrs=attrs, choices=choices) - super(DropDownMultiple, self).__init__(attrs) + return nonjs_output + js_output - def render(self, name, value, attrs=None, choices=()): + def render_js(self, name, value, attrs=None, choices=()): if value is None: value = [] final_attrs = self.build_attrs(attrs, name=name) @@ -60,17 +76,17 @@ class DropDownMultiple(widgets.Widget): del final_attrs['id'] # Insert blank value - choices = [('','---')] + list(self.choices) + choices = [('','---')] + list(choices) # Build values items = [] for val in value: - opts = "\n".join([TPL_OPTION %{'value': k, 'desc': v, 'selected': val == k and 'selected="selected"' or ''} for k, v in choices]) + opts = self.render_options(choices, [val]) items.append(TPL_SELECT %{'attrs': flatatt(final_attrs), 'opts': opts}) # Build blank value - opts = "\n".join([TPL_OPTION %{'value': k, 'desc': v, 'selected': ''} for k, v in choices]) + opts = self.render_options(choices, ['']) items.append(TPL_SELECT %{'attrs': flatatt(final_attrs), 'opts': opts}) script = TPL_SCRIPT %{'id': id} @@ -78,8 +94,9 @@ class DropDownMultiple(widgets.Widget): return mark_safe(output) - def value_from_datadict(self, data, files, name): - if isinstance(data, MultiValueDict): - return [i for i in data.getlist(name) if i] - - return data.get(name, None) + def value_from_datadict(self, *args, **kwargs): + # Let our parent take care of this, but filter out the empty + # value (which is usually present from the last unused + # dropdown). + values = super(DropDownMultiple, self).value_from_datadict(*args, **kwargs) + return [i for i in values if i] -- 2.30.2