diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-05-23 17:13:35 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-06-12 08:49:06 +0200 |
commit | d807e2b7b96935174135bc76b6a22f3a0100bd95 (patch) | |
tree | 427ba1a5c67ec7d578549f64dbd3496d72ec37ca /ishtar_common | |
parent | 88c9aeaeafd9bc77decbf0982be0fd9d20fd3993 (diff) | |
download | Ishtar-d807e2b7b96935174135bc76b6a22f3a0100bd95.tar.bz2 Ishtar-d807e2b7b96935174135bc76b6a22f3a0100bd95.zip |
Wizards - JSON fields: Manage field display in forms - management in wizards save and form init (refs #4089)
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/forms.py | 78 | ||||
-rw-r--r-- | ishtar_common/migrations/0053_auto_20180523_1504.py (renamed from ishtar_common/migrations/0053_auto_20180523_1126.py) | 4 | ||||
-rw-r--r-- | ishtar_common/models.py | 3 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 45 |
4 files changed, 116 insertions, 14 deletions
diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index c314e4f13..5de0db91d 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -20,6 +20,7 @@ """ Forms definition """ +from collections import OrderedDict import datetime import re import types @@ -107,6 +108,16 @@ def get_readonly_clean(key): return func +JSON_VALUE_TYPES_FIELDS = { + 'T': (forms.CharField, None), + 'LT': (forms.CharField, forms.Textarea), + 'I': (forms.IntegerField, None), + 'F': (forms.FloatField, None), + 'D': (DateField, None), + 'C': (forms.CharField, None), +} + + class CustomForm(object): form_admin_name = "" form_slug = "" @@ -120,8 +131,7 @@ class CustomForm(object): except AttributeError: pass super(CustomForm, self).__init__(*args, **kwargs) - available, excluded = self.check_availability_and_excluded_fields( - current_user) + available, excluded, json_fields = self.check_custom_form(current_user) for exc in excluded: if hasattr(self, 'fields'): self.remove_field(exc) @@ -131,6 +141,25 @@ class CustomForm(object): if exc in form.fields: form.fields.pop(exc) + new_fields = {} + for order, key, field in json_fields: + while order in new_fields: # json fields with the same number + order += 1 + new_fields[order] = (key, field) + + if not new_fields: + return + + # re-order for json fields + fields = OrderedDict() + for idx, field in enumerate(self.fields.items()): + key, c_field = field + if idx + 1 in new_fields: + alt_key, alt_field = new_fields[idx + 1] + fields[alt_key] = alt_field + fields[key] = c_field + self.fields = fields + def are_available(self, keys): for k in keys: if k not in self.fields: @@ -142,9 +171,28 @@ class CustomForm(object): self.fields.pop(key) @classmethod - def check_availability_and_excluded_fields(cls, current_user): + def _get_json_fields(cls, custom_form): + fields = [] + for field in custom_form.json_fields.order_by('order').all(): + key = "data__" + field.json_field.key + field_cls, widget = forms.CharField, None + if field.json_field.value_type in JSON_VALUE_TYPES_FIELDS: + field_cls, widget = JSON_VALUE_TYPES_FIELDS[ + field.json_field.value_type] + attrs = {'label': field.label or field.json_field.name, + 'required': False} + if field.help_text: + attrs['help_text'] = field.help_text + if widget: + attrs['widget'] = widget(attrs={"class": "form-control"}) + f = field_cls(**attrs) + fields.append((field.order or 1, key, f)) + return fields + + @classmethod + def check_custom_form(cls, current_user): if not current_user: - return True, [] + return True, [], [] base_q = {"form": cls.form_slug, 'available': True} # order is important : try for user, user type then all query_dicts = [] @@ -159,23 +207,31 @@ class CustomForm(object): dct = base_q.copy() dct.update({'apply_to_all': True}) query_dicts.append(dct) - excluded_lst = [] + form = None for query_dict in query_dicts: q = models.CustomForm.objects.filter(**query_dict) if not q.count(): continue # todo: prevent multiple result in database form = q.all()[0] - if not form.enabled: - return False, [] - for excluded in form.excluded_fields.all(): - # could have be filtered previously - excluded_lst.append(excluded.field) break - return True, excluded_lst + if not form: + return True, [], [] + if not form.enabled: + return False, [], [] + excluded_lst = [] + for excluded in form.excluded_fields.all(): + # could have be filtered previously + excluded_lst.append(excluded.field) + json_fields = cls._get_json_fields(form) + return True, excluded_lst, json_fields @classmethod def get_custom_fields(cls): + """ + Get fields than can be customized: excluded, re-ordered (WIP) or + re-labeled (WIP) + """ if hasattr(cls, 'base_fields'): fields = cls.base_fields else: diff --git a/ishtar_common/migrations/0053_auto_20180523_1126.py b/ishtar_common/migrations/0053_auto_20180523_1504.py index f776dce30..14e34a866 100644 --- a/ishtar_common/migrations/0053_auto_20180523_1126.py +++ b/ishtar_common/migrations/0053_auto_20180523_1504.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.10 on 2018-05-23 11:26 +# Generated by Django 1.11.10 on 2018-05-23 15:04 from __future__ import unicode_literals from django.db import migrations, models @@ -17,7 +17,9 @@ class Migration(migrations.Migration): name='CustomFormJsonField', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.CharField(blank=True, default=b'', max_length=200, verbose_name='Label')), ('order', models.IntegerField(default=1, verbose_name='Order')), + ('help_text', models.TextField(blank=True, null=True, verbose_name='Help')), ('custom_form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='json_fields', to='ishtar_common.CustomForm')), ], options={ diff --git a/ishtar_common/models.py b/ishtar_common/models.py index bb285883f..e4c6d0e9a 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -1893,7 +1893,10 @@ class CustomFormJsonField(models.Model): custom_form = models.ForeignKey(CustomForm, related_name='json_fields') json_field = models.ForeignKey(JsonDataField, related_name='custom_form_details') + label = models.CharField(_(u"Label"), max_length=200, blank=True, + default='') order = models.IntegerField(verbose_name=_(u"Order"), default=1) + help_text = models.TextField(_(u"Help"), blank=True, null=True) class Meta: verbose_name = _(u"Custom form - Json data field") diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index a6844b674..40044f100 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -120,8 +120,12 @@ def filter_no_fields_form(form, other_check=None): if not hasattr(self.request.user, 'ishtaruser'): return False if issubclass(form, CustomForm): - enabled, exc = form.check_availability_and_excluded_fields( + enabled, excluded, json_fields = form.check_custom_form( self.request.user.ishtaruser) + if not hasattr(self, 'json_fields'): + self.json_fields = {} + self.json_fields[form.form_slug] = [ + key for order, key, field in json_fields] if not enabled: return False if other_check: @@ -595,11 +599,27 @@ class Wizard(NamedUrlWizardView): return_object): dct = self.get_extra_model(dct, form_list) obj = self.get_current_saved_object() - # manage dependant items + data = {} + if obj: + data = obj.data + + # manage dependant items and json fields other_objs = {} for k in dct.keys(): if '__' not in k: continue + # manage json field + if k.startswith('data__'): + data_keys = k[len('data__'):].split('__') + # tree + current_data = data + for data_key in data_keys[:-1]: + if data_key not in current_data: + current_data[data_key] = {} + current_data = current_data[data_key] + current_data[data_keys[-1]] = dct.pop(k) + continue + vals = k.split('__') assert len(vals) == 2, \ "Only one level of dependant item is managed" @@ -627,6 +647,7 @@ class Wizard(NamedUrlWizardView): elif type(dct[k]) not in (list, tuple): dct[k] = [dct[k]] setattr(obj, k, dct[k]) + obj.data = data if hasattr(obj, 'pre_save'): obj.pre_save() try: @@ -711,6 +732,7 @@ class Wizard(NamedUrlWizardView): except ValidationError as e: logger.warning(unicode(e)) return self.render(form_list[-1]) + obj.data = data obj.save(**saved_args) for k in adds: getattr(obj, k).add(adds[k]) @@ -1243,6 +1265,25 @@ class Wizard(NamedUrlWizardView): Get initial data from an object: simple form """ initial = MultiValueDict() + + # manage json field + if hasattr(self, 'json_fields') \ + and getattr(c_form, 'form_slug', None) \ + and c_form.form_slug in self.json_fields \ + and obj.data: + for key in self.json_fields[c_form.form_slug]: + if not key.startswith('data__'): + continue + json_keys = key[len('data__'):].split('__') + value = obj.data + for json_key in json_keys: + if json_key not in value: + value = None + break + value = value[json_key] + if value: + initial[key] = value + for base_field in c_form.base_fields.keys(): value = obj base_model = None |