summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2016-06-27 20:31:19 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2016-06-27 20:31:19 +0200
commit3cc6f7df95ec2c82689f227843d4ed0b7177e844 (patch)
treee5e53acfe3c489bea7f2aad38a01f84afb607064
parent6793ee9d4010d5b5399e8c27979d3f7d1d88f8f8 (diff)
downloadChimère-3cc6f7df95ec2c82689f227843d4ed0b7177e844.tar.bz2
Chimère-3cc6f7df95ec2c82689f227843d4ed0b7177e844.zip
Add an autocomplete field
-rw-r--r--chimere/forms.py8
-rw-r--r--chimere/models.py5
-rw-r--r--chimere/templates/chimere/blocks/JQueryAutoComplete.html57
-rw-r--r--chimere/urls.py3
-rw-r--r--chimere/views.py22
-rw-r--r--chimere/widgets.py69
6 files changed, 158 insertions, 6 deletions
diff --git a/chimere/forms.py b/chimere/forms.py
index 599f88a..ae0092f 100644
--- a/chimere/forms.py
+++ b/chimere/forms.py
@@ -393,9 +393,15 @@ def get_properties(queryset):
label=prop.name, choices=[('', '--')] +
[(choice.pk, unicode(choice)) for choice in choices],
required=False)
+ elif prop.type == 'A':
+ widget = PropertyModel.TYPE_WIDGET[prop.type]
+ widget = widget(slug=prop.slug)
+ fields[key] = forms.CharField(label=prop.name, widget=widget,
+ required=False)
else:
+ widget = PropertyModel.TYPE_WIDGET[prop.type]
fields[key] = forms.CharField(
- label=prop.name, widget=PropertyModel.TYPE_WIDGET[prop.type],
+ label=prop.name, widget=widget,
required=False)
return fields
diff --git a/chimere/models.py b/chimere/models.py
index d16d20a..9e7e52f 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -44,7 +44,8 @@ from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _
from chimere.widgets import HiddenPointChooserWidget, PointField, RouteField, \
- SelectMultipleField, TextareaWidget, DatePickerWidget, PolygonField
+ SelectMultipleField, TextareaWidget, DatePickerWidget, PolygonField, \
+ JQueryAutoComplete
from chimere.utils import KMLManager, OSMManager, ShapefileManager, \
GeoRSSManager, CSVManager, HtmlXsltManager, XMLXsltManager, JsonManager, \
IcalManager
@@ -1954,6 +1955,7 @@ class PropertyModel(models.Model):
('P', _('Password')),
('D', _("Date")),
('C', _("Choices")),
+ ('A', _("Choices (autocomplete)")),
('B', _("Boolean")),
)
TYPE_WIDGET = {'T': forms.TextInput,
@@ -1961,6 +1963,7 @@ class PropertyModel(models.Model):
'P': forms.PasswordInput,
'D': DatePickerWidget,
'C': forms.Select,
+ 'A': JQueryAutoComplete,
'B': forms.CheckboxInput,
}
type = models.CharField(_(u"Type"), max_length=1, choices=TYPE)
diff --git a/chimere/templates/chimere/blocks/JQueryAutoComplete.html b/chimere/templates/chimere/blocks/JQueryAutoComplete.html
new file mode 100644
index 0000000..c316869
--- /dev/null
+++ b/chimere/templates/chimere/blocks/JQueryAutoComplete.html
@@ -0,0 +1,57 @@
+<input{{attrs_select|safe}}/>
+<input type="hidden"{{attrs_hidden|safe}}/>
+ <script type="text/javascript"><!--//
+
+$(function() {
+ $('#id_select_{{field_id}}').autocomplete({
+ autoFocus: true,
+ source:{{source|safe}},
+ select: function (event, ui){
+ if(ui.item){
+ $('#id_{{field_id}}').val(ui.item.id);
+ } else {
+ $('#id_{{field_id}}').val(null);
+ $('#id_select_{{field_id}}').val('');
+ }
+ },
+ minLength: 2,
+ change: function (event, ui){
+ if(ui.item){
+ $('#id_{{field_id}}').val(ui.item.id);
+ } else {
+ $('#id_{{field_id}}').val(null);
+ $('#id_select_{{field_id}}').val('');
+ }
+ },
+ open: function(){
+ setTimeout(function () {
+ $('.ui-autocomplete').css('z-index', 1000);
+ }, 0);
+ }{% if options %},
+ {{options}}{% endif %}
+ });
+
+ $('#id_select_{{field_id}}').on('click', function(){
+ $('#id_{{field_id}}').val(null);
+ $('#id_select_{{field_id}}').val(null);
+ });
+
+ $('body').on('click', function(){
+ if (!$('#id_{{field_id}}').val()) {
+ $('#id_select_{{field_id}}').val(null);
+ }
+ });
+
+ $('#id_select_{{field_id}}').on( "autocompletefocus",
+ function( event, ui ){
+ disable_enter = true;
+ } );
+
+ $('#id_select_{{field_id}}').on( "autocompleteselect",
+ function( event, ui ){
+ disable_enter = false;
+ } );
+
+});
+//--></script>
+
diff --git a/chimere/urls.py b/chimere/urls.py
index de7f3f5..966ef4f 100644
--- a/chimere/urls.py
+++ b/chimere/urls.py
@@ -147,6 +147,9 @@ urlpatterns += patterns(
url(r'^(?:(?P<area_name>[a-zA-Z0-9_-]*)/)?categories/'
r'(?P<category_slug>[a-zA-Z0-9_-]+)$',
CategoryView.as_view(), name='category-directory-detail'),
+ url(r'^(?:(?P<area_name>[a-zA-Z0-9_-]*)/)?property-choices/'
+ r'(?P<property_slug>[a-zA-Z0-9_-]+)/$',
+ 'property_choice_list', name='property-choices'),
# At the end, because it catches large
url(r'^(?P<area_name>[a-zA-Z0-9_-]+)?', 'index', name="index"),
)
diff --git a/chimere/views.py b/chimere/views.py
index 7274bf0..40db95a 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -47,7 +47,7 @@ from django.views.generic import TemplateView, ListView
from chimere.actions import actions
from chimere.models import Category, SubCategory, PropertyModel, Page,\
Marker, Route, Polygon, SimpleArea, Area, Color, TinyUrl, RouteFile,\
- AggregatedRoute, AggregatedPolygon
+ AggregatedRoute, AggregatedPolygon, PropertyModelChoice
from chimere.widgets import PointChooserWidget, RouteChooserWidget, AreaWidget,\
PolygonChooserWidget
@@ -1264,6 +1264,26 @@ def rss(request, area_name=''):
# from django.core.paginator import Paginator, InvalidPage
+
+def property_choice_list(request, area_name='', property_slug=''):
+ data = {}
+ try:
+ pm = PropertyModel.objects.get(slug=property_slug, available=True)
+ except PropertyModel.DoesNotExist:
+ return HttpResponse(json.dumps(data),
+ content_type="application/json")
+ if not request.GET or not request.GET.get('term') or \
+ pm.areas.count() and not pm.areas.filter(urn=area_name).count():
+ return HttpResponse(json.dumps(data),
+ content_type="application/json")
+ fltr = {'propertymodel': pm, 'available': True,
+ 'value__icontains': request.GET.get('term')}
+ q = PropertyModelChoice.objects.filter(**fltr).order_by('value')
+ data = [{"id": p.pk, "value": p.value} for p in q.all()]
+ return HttpResponse(json.dumps(data),
+ content_type="application/json")
+
+
SearchView = None
autocomplete = None
if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \
diff --git a/chimere/widgets.py b/chimere/widgets.py
index 78d7508..2672ba0 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -20,15 +20,18 @@
"""
Extra widgets and fields
"""
+
+from json import JSONEncoder
+
from django import forms
from django.conf import settings
from django.contrib.gis.db import models
from django.contrib.gis.geos import fromstr
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
-from django.forms.widgets import RadioInput, RadioFieldRenderer
-from django.utils.encoding import force_unicode
-from django.utils.html import conditional_escape
+from django.forms.widgets import RadioInput, RadioFieldRenderer, flatatt
+from django.utils.encoding import force_unicode, smart_unicode
+from django.utils.html import conditional_escape, escape
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
@@ -262,6 +265,66 @@ class DatePickerWidget(forms.TextInput):
return mark_safe(rendered)
+class JQueryAutoComplete(forms.TextInput):
+ TEMPLATE = "chimere/blocks/JQueryAutoComplete.html"
+
+ def __init__(self, slug, options={}, attrs={}):
+ """
+ Source can be a list containing the autocomplete values or a
+ string containing the url used for the request.
+ """
+ self.options = None
+ self.attrs = {}
+ self.slug = slug
+ if len(options) > 0:
+ self.options = JSONEncoder().encode(options)
+ self.attrs.update(attrs)
+
+ def get_source(self):
+ # Strange... to be fixed
+ source = reverse('chimere:property-choices',
+ kwargs={'property_slug': self.slug})
+ return "'{}'".format(source)
+
+ def render(self, name, value=None, attrs=None):
+ attrs_hidden = self.build_attrs(attrs, name=name)
+ attrs_select = self.build_attrs(attrs)
+ selected_value, rendered_value = "", ""
+
+ if value:
+ val = escape(smart_unicode(value))
+ attrs_hidden['value'] = val
+ attrs_select['value'] = val
+ selected_value = val
+ if val:
+ from chimere.models import PropertyModelChoice
+ try:
+ attrs_select['value'] = unicode(
+ PropertyModelChoice.objects.get(
+ pk=value, propertymodel__slug=self.slug))
+ rendered_value = attrs_select['value']
+ except:
+ attrs_select['value'] = ""
+ if 'id' not in self.attrs:
+ attrs_hidden['id'] = 'id_%s' % name
+ attrs_select['id'] = 'id_select_%s' % name
+ if 'class' not in attrs_select:
+ attrs_select['class'] = 'autocomplete'
+ dct = {
+ 'attrs_select': flatatt(attrs_select),
+ 'attrs_hidden': flatatt(attrs_hidden),
+ 'field_id': name,
+ 'min_field_id': name.replace('_', ''),
+ 'options': self.options,
+ 'source': self.get_source(),
+ 'selected_value': selected_value,
+ 'rendered_value': rendered_value
+ }
+
+ return mark_safe(
+ render_to_string(self.TEMPLATE, dct))
+
+
class NominatimWidget(forms.TextInput):
class Media:
js = ["%schimere/js/nominatim-widget.js" % settings.STATIC_URL]