+# 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 = """<option value="%(value)s" %(selected)s>%(desc)s</option>"""
TPL_SELECT = """
-<select class="dropdown_select" %(attrs)s>
+<select class="dropdown_select" %(attrs)s disabled="disabled">
%(opts)s
</select>
"""
TPL_SCRIPT = """
<script>
- $('span#%(id)s>select.dropdown_select').change(function(){
- var pattern = 'span#%(id)s>select.dropdown_select';
+ $('span#%(id)s_multiple>select.dropdown_select').change(function(){
+ var pattern = 'span#%(id)s_multiple>select.dropdown_select';
var last_item = $(pattern+':last');
if (last_item.val()) {
- last_item.clone(true).appendTo($('span#%(id)s'));
- $('span#%(id)s').append(' ');
+ last_item.clone(true).appendTo($('span#%(id)s_multiple'));
+ $('span#%(id)s_multiple').append(' ');
};
var values = [];
}
};
});
+ $(document).ready(function(){
+ // Since we do graceful fallback, the JScript driven interface
+ // is hidden and disabled by default and the plain HTML
+ // interface is shown. Here we swap them around.
+ $('select#%(id)s').hide();
+ $('select#%(id)s').attr('disabled', true);
+ $('span#%(id)s_multiple').show();
+ $('span#%(id)s_multiple>select.dropdown_select').attr('disabled', false);
+ });
</script>
"""
TPL_FULL = """
-<span class="dropdown_multiple" id="%(id)s">
+<span class="dropdown_multiple" id="%(id)s_multiple" style="display:none">
%(values)s
%(script)s
</span>
"""
-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)
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}
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]