summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES-DEV.md3
-rw-r--r--ishtar_common/forms.py7
-rw-r--r--ishtar_common/models.py1
-rw-r--r--ishtar_common/models_common.py16
-rw-r--r--ishtar_common/widgets.py45
5 files changed, 61 insertions, 11 deletions
diff --git a/CHANGES-DEV.md b/CHANGES-DEV.md
index 5159f6ff0..8ada64dca 100644
--- a/CHANGES-DEV.md
+++ b/CHANGES-DEV.md
@@ -2,7 +2,8 @@ Ishtar changelog
================
### Features ###
-- Context record: bulk update
+- Context record: bulk update relation - type
+- JSON types: multi valued choices
### Bugs ###
- Search: fix url for person and organization
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py
index 31d1004c7..b92a80f42 100644
--- a/ishtar_common/forms.py
+++ b/ishtar_common/forms.py
@@ -136,6 +136,7 @@ JSON_VALUE_TYPES_FIELDS = {
"D": (DateField, None),
"B": (forms.NullBooleanField, None),
"C": (widgets.Select2DynamicField, None),
+ "MC": (widgets.Select2DynamicMultipleField, None),
}
@@ -312,8 +313,10 @@ class CustomForm(BSForm):
attrs["help_text"] = field["help_text"]
if widget:
attrs["widget"] = widget()
- if field_cls == widgets.Select2DynamicField:
- attrs["choices"] = cls._get_dynamic_choices(key)
+ if field_cls in (widgets.Select2DynamicField,
+ widgets.Select2DynamicMultipleField):
+ choices = cls._get_dynamic_choices(key)
+ attrs["choices"] = choices
f = field_cls(**attrs)
kls = "form-control"
if "class" in f.widget.attrs:
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 6007cadef..0e3051e28 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -674,6 +674,7 @@ JSON_VALUE_TYPES = (
("F", _("Float")),
("D", _("Date")),
("C", _("Choices")),
+ ("MC", _("Multi-choices")),
)
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index fe8e65bb1..dc078000e 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -1077,12 +1077,22 @@ class JsonData(models.Model, CachedGen):
q = cls.objects.filter(data__has_key=key[len("data__") :]).values_list(
"data", flat=True
)
+ multi = False
for value in q.all():
for k in splitted_key:
value = value[k]
- choices.add(value)
- choices = [("", "")] + [(v, v) for v in sorted(list(choices))]
- cache.set(cache_key, choices, settings.CACHE_SMALLTIMEOUT)
+ if isinstance(value, list):
+ multi = True
+ for v in value:
+ if v:
+ choices.add(v)
+ else:
+ choices.add(value)
+ c = []
+ if not multi:
+ c = [("", "")]
+ c += [(v, v) for v in sorted(list(choices))]
+ cache.set(cache_key, c, settings.CACHE_SMALLTIMEOUT)
return choices
diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py
index 008925276..40df75a32 100644
--- a/ishtar_common/widgets.py
+++ b/ishtar_common/widgets.py
@@ -126,16 +126,23 @@ class Select2Media(object):
return media
-class Select2Dynamic(Select2Media, forms.Select):
+class Select2DynamicBase(Select2Media):
"""
Select input using select, allowing dynamic creation.
"""
+ MULTIPLE = False
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
+ if value:
+ values = [value]
+ if self.MULTIPLE:
+ value = value[0]
+ values = value
+ for va in values:
+ if va not in [key for key, va in choices]:
+ choices.insert(1, (va, va))
+ self.choices = choices
klass = attrs and attrs.get("class") or ""
klass += " " if klass else "" + "js-select2"
if not attrs:
@@ -161,7 +168,9 @@ class Select2Dynamic(Select2Media, forms.Select):
if attrs.get("full-width", None):
options.append("containerCssClass: 'full-width'")
- html = super(Select2Dynamic, self).render(name, value, attrs)
+ if self.MULTIPLE:
+ options.append("multiple: 'true'")
+ html = super(Select2DynamicBase, self).render(name, value, attrs)
html += """<script type="text/javascript">
$(document).ready(function() {{
$("#id_{}").select2({{ {} }});
@@ -172,6 +181,14 @@ class Select2Dynamic(Select2Media, forms.Select):
return mark_safe(html)
+class Select2Dynamic(Select2DynamicBase, forms.Select):
+ MULTIPLE = False
+
+
+class Select2DynamicMultiple(Select2DynamicBase, forms.SelectMultiple):
+ MULTIPLE = True
+
+
class Select2DynamicField(forms.ChoiceField):
widget = Select2Dynamic
@@ -190,6 +207,24 @@ class Select2DynamicField(forms.ChoiceField):
return super(Select2DynamicField, self).to_python(value).strip()
+class Select2DynamicMultipleField(forms.MultipleChoiceField):
+ widget = Select2DynamicMultiple
+
+ def validate(self, value):
+ """
+ Key can be added dynamically. Only check that the character " is not
+ used.
+ """
+ if value and '"' in value:
+ raise ValidationError(_('The character " is not accepted.'))
+
+ def to_python(self, value):
+ """
+ Strip value
+ """
+ return [v.strip() for v in super().to_python(value)]
+
+
class Select2Base(Select2Media):
def __init__(
self, attrs=None, choices=(), remote=None, model=None, new=None, available=None