From 39e296ed421cce7696313dcafe75cd03d073054d Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Thu, 26 Dec 2013 19:09:09 +0100 Subject: Manage archaeological sites into forms (refs #1586) * create new widget: multiple autocomplete field * move JS autocomplete to template * archaeological site reference made unique --- ishtar_common/widgets.py | 113 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 26 deletions(-) (limited to 'ishtar_common/widgets.py') diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index a97cfe70b..fc3ada283 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -27,6 +27,7 @@ from django.forms import ClearableFileInput from django.forms.widgets import flatatt from django.template import Context, loader from django.utils.encoding import smart_unicode +from django.utils.functional import lazy from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.simplejson import JSONEncoder @@ -34,6 +35,44 @@ from django.utils.translation import ugettext_lazy as _ import models +reverse_lazy = lazy(reverse, unicode) + +class MultipleAutocompleteField(forms.MultipleChoiceField): + def __init__(self, *args, **kwargs): + model = None + if 'model' in kwargs: + model = kwargs.pop('model') + if 'choices' not in kwargs and model: + kwargs['choices'] = [(i.pk, unicode(i))for i in model.objects.all()] + new = kwargs.pop('new') if 'new' in kwargs else None + if 'widget' not in kwargs and model: + kwargs['widget'] = JQueryAutoComplete(reverse_lazy( + 'autocomplete-'+model.__name__.lower()), + associated_model=model, new=new, + multiple=True) + super(MultipleAutocompleteField, self).__init__(*args, **kwargs) + + def clean(self, value): + if value: + # clean JS messup with values + try: + if type(value) not in (list, tuple): + value = [value] + else: + val = value + value = [] + for v in val: + v = unicode(v).strip('[').strip(']' + ).strip('u').strip("'").strip('"') + value += [int(v.strip()) + for v in list(set(v.split(','))) + if v.strip()] + except (TypeError, ValueError): + value = [] + else: + value = [] + return super(MultipleAutocompleteField, self).clean(value) + class DeleteWidget(forms.CheckboxInput): def render(self, name, value, attrs=None): final_attrs = flatatt(self.build_attrs(attrs, name=name, @@ -95,7 +134,7 @@ class JQueryDate(forms.TextInput): class JQueryAutoComplete(forms.TextInput): def __init__(self, source, associated_model=None, options={}, attrs={}, - new=False): + new=False, multiple=False): """ Source can be a list containing the autocomplete values or a string containing the url used for the request. @@ -108,6 +147,13 @@ class JQueryAutoComplete(forms.TextInput): self.options = JSONEncoder().encode(options) self.attrs.update(attrs) self.new = new + self.multiple = multiple + + def value_from_datadict(self, data, files, name): + if self.multiple: + return data.getlist(name, None) + else: + return data.get(name, None) def render_js(self, field_id): if isinstance(self.source, list): @@ -119,39 +165,54 @@ class JQueryAutoComplete(forms.TextInput): source = "'" + unicode(self.source) + "'" except: raise ValueError('source type is not valid') - options = 'source : ' + source - options += ''', select: function( event, ui ) { - if(ui.item){ - $('#id_%s').val(ui.item.id); - } else { - $('#id_%s').val(null); - } - }, minLength: 2 - ''' % (field_id, field_id) + dct = {'source':mark_safe(source), + 'field_id':field_id} if self.options: - options += ',%s' % self.options + dct['options'] = mark_safe('%s' % self.options) - js = u'$(\'#id_select_%s\').autocomplete({%s});\n' % (field_id, options) - js += u'''$(\'#id_select_%s\').live('click', function(){ - $('#id_%s').val(null); - $('#id_select_%s').val(null); -});''' % (field_id, field_id, field_id) + js = "" + tpl = 'blocks/JQueryAutocomplete.js' + if self.multiple: + tpl = 'blocks/JQueryAutocompleteMultiple.js' + t = loader.get_template(tpl) + js = t.render(Context(dct)) return js def render(self, name, value=None, attrs=None): attrs_hidden = self.build_attrs(attrs, name=name) attrs_select = self.build_attrs(attrs) - if value: - val = escape(smart_unicode(value)) - attrs_hidden['value'] = val - attrs_select['value'] = val - if self.associated_model: - try: - attrs_select['value'] = unicode( - self.associated_model.objects.get(pk=value)) - except: - attrs_select['value'] = "" + hiddens = [] + selects = [] + values = value + if type(value) not in (list, tuple): + values = unicode(escape(smart_unicode(value))) + values = values.replace('[', '').replace(']', '') + values = values.split(',') + else: + values = [] + for v in value: + values += v.split(',') + for v in values: + if not v: + continue + hiddens.append(v) + selects.append(v) + if self.associated_model: + try: + selects[-1] = unicode( + self.associated_model.objects.get(pk=v)) + except (self.associated_model.DoesNotExist, ValueError): + selects.pop() + hiddens.pop() + if self.multiple: + attrs_hidden['value'] = ", ".join(hiddens) + if selects: + selects.append("") + attrs_select['value'] = ", ".join(selects) + else: + attrs_hidden['value'] = hiddens[0] + attrs_select['value'] = selects[0] if not self.attrs.has_key('id'): attrs_hidden['id'] = 'id_%s' % name attrs_select['id'] = 'id_select_%s' % name -- cgit v1.2.3