summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2018-05-24 12:45:09 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2018-06-12 08:49:06 +0200
commitc34b5eb03df8d4287797704ef48b31192f73d949 (patch)
tree7f2ead38802ae262f6a22abecacd7ce42fa96600 /ishtar_common
parentc80d0b8b3571b1055951ee8e387ec6ee56d5c55b (diff)
downloadIshtar-c34b5eb03df8d4287797704ef48b31192f73d949.tar.bz2
Ishtar-c34b5eb03df8d4287797704ef48b31192f73d949.zip
Wizards - JSON fields: Manage dynamic list - list existing + dynamic add (refs #4089)
Diffstat (limited to 'ishtar_common')
-rw-r--r--ishtar_common/forms.py29
-rw-r--r--ishtar_common/widgets.py87
2 files changed, 105 insertions, 11 deletions
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py
index 5de0db91d..59f3e141a 100644
--- a/ishtar_common/forms.py
+++ b/ishtar_common/forms.py
@@ -26,6 +26,7 @@ import re
import types
from django import forms
+from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.core import validators
from django.forms.formsets import BaseFormSet, DELETION_FIELD_NAME
@@ -114,7 +115,7 @@ JSON_VALUE_TYPES_FIELDS = {
'I': (forms.IntegerField, None),
'F': (forms.FloatField, None),
'D': (DateField, None),
- 'C': (forms.CharField, None),
+ 'C': (widgets.Select2DynamicField, None),
}
@@ -171,6 +172,30 @@ class CustomForm(object):
self.fields.pop(key)
@classmethod
+ def _get_dynamic_choices(cls, key):
+ """
+ Get choice from existing values
+ :param key: data key
+ :return: tuple of choices (id, value)
+ """
+ app_name = cls.__module__.split('.')[0]
+ if app_name == "archaeological_files_pdl":
+ app_name = "archaeological_files"
+ model_name = cls.form_slug.split("-")[0].replace('_', "")
+ ct = ContentType.objects.get(app_label=app_name, model=model_name)
+ ct_class = ct.model_class()
+ choices = set()
+ splitted_key = key[len('data__'):].split('__')
+ for obj in ct_class.objects.filter(
+ data__has_key=key[len('data__'):]).all():
+ value = obj.data
+ for k in splitted_key:
+ value = value[k]
+ choices.add(value)
+ choices = [('--', '')] + [(v, v) for v in sorted(list(choices))]
+ return choices
+
+ @classmethod
def _get_json_fields(cls, custom_form):
fields = []
for field in custom_form.json_fields.order_by('order').all():
@@ -185,6 +210,8 @@ class CustomForm(object):
attrs['help_text'] = field.help_text
if widget:
attrs['widget'] = widget(attrs={"class": "form-control"})
+ if field_cls == widgets.Select2DynamicField:
+ attrs['choices'] = cls._get_dynamic_choices(key)
f = field_cls(**attrs)
fields.append((field.order or 1, key, f))
return fields
diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py
index 442f12b9a..a20d33fc3 100644
--- a/ishtar_common/widgets.py
+++ b/ishtar_common/widgets.py
@@ -23,6 +23,7 @@ import logging
from django import forms
from django.conf import settings
+from django.core.exceptions import ValidationError
from django.core.files import File
from django.core.urlresolvers import reverse, NoReverseMatch
from django.db.models import fields
@@ -101,18 +102,10 @@ class SelectReadonlyField(forms.ChoiceField):
return bool(self.get_q().filter(pk=value).count())
-class Select2Multiple(forms.SelectMultiple):
- def __init__(self, attrs=None, choices=(), remote=None, model=None,
- new=None, available=None):
- super(Select2Multiple, self).__init__(attrs, choices)
- self.remote = remote
- self.available = available
- self.model = model
- self.new = new
-
+class Select2Media(object):
@property
def media(self):
- media = super(Select2Multiple, self).media
+ media = super(Select2Media, self).media
css = {
'all': ('select2/css/select2.css',)
}
@@ -123,6 +116,80 @@ class Select2Multiple(forms.SelectMultiple):
media.add_js(js)
return media
+
+class Select2Dynamic(Select2Media, forms.Select):
+ """
+ Select input using select, allowing dynamic creation.
+ """
+
+ def render(self, name, value, attrs=None, choices=()):
+ choices = choices or getattr(self, 'choices', [])
+ if value and value not in [key for key, v in choices]:
+ choices.insert(1, (value, value))
+ self.choices = choices
+ klass = attrs and attrs.get('class') or ''
+ klass += ' ' if klass else '' + 'js-select2'
+ if not attrs:
+ attrs = {}
+ attrs['class'] = klass
+ if 'style' not in attrs:
+ if attrs.get('full-width', None):
+ attrs['style'] = "width: calc(100% - 60px)"
+ else:
+ attrs['style'] = "width: 370px"
+ options = [
+ u"tags: true",
+ ]
+ '''
+ msg = unicode(
+ _(u"Are you sure you want to add this term? (the addition is "
+ u"effective after registration of the element)")
+ )
+ options.append(u"""createTag: function (params) {{
+ return confirm("{}");
+ }}""".format(msg))
+ '''
+ if attrs.get('full-width', None):
+ options.append(u"containerCssClass: 'full-width'")
+
+ html = super(Select2Dynamic, self).render(name, value, attrs)
+ html += """<script type="text/javascript">
+ $(document).ready(function() {{
+ $("#id_{}").select2({{ {} }});
+ }});</script>
+ """.format(name, u", ".join(options))
+ return mark_safe(html)
+
+
+class Select2DynamicField(forms.ChoiceField):
+ widget = Select2Dynamic
+
+ def validate(self, value):
+ """
+ Key can be added dynamically. Only check that the character " is not
+ used.
+ """
+ if value and u'"' in value:
+ raise ValidationError(
+ _(u"The character \" is not accepted.")
+ )
+
+ def to_python(self, value):
+ """
+ Strip value
+ """
+ return super(Select2DynamicField, self).to_python(value).strip()
+
+
+class Select2Multiple(Select2Media, forms.SelectMultiple):
+ def __init__(self, attrs=None, choices=(), remote=None, model=None,
+ new=None, available=None):
+ super(Select2Multiple, self).__init__(attrs, choices)
+ self.remote = remote
+ self.available = available
+ self.model = model
+ self.new = new
+
def get_q(self):
q = self.model.objects
if self.available: