summaryrefslogtreecommitdiff
path: root/ishtar/ishtar_base/forms.py
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2011-06-24 14:37:16 +0200
committerÉtienne Loks <etienne.loks@peacefrogs.net>2011-06-24 14:37:16 +0200
commit05c6d94c9547377c9979e9d860c6618ee898ef6e (patch)
tree6b4fc14f42da9d91ab2bb4b989ffeeb42947392f /ishtar/ishtar_base/forms.py
parent2d008477cb66ec3e356fd9153afba7affede249c (diff)
downloadIshtar-05c6d94c9547377c9979e9d860c6618ee898ef6e.tar.bz2
Ishtar-05c6d94c9547377c9979e9d860c6618ee898ef6e.zip
Sources creation for Operation (refs #497) - restructuration (refs #57)
Diffstat (limited to 'ishtar/ishtar_base/forms.py')
-rw-r--r--ishtar/ishtar_base/forms.py736
1 files changed, 736 insertions, 0 deletions
diff --git a/ishtar/ishtar_base/forms.py b/ishtar/ishtar_base/forms.py
new file mode 100644
index 000000000..29d9ab4d6
--- /dev/null
+++ b/ishtar/ishtar_base/forms.py
@@ -0,0 +1,736 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010-2011 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# See the file COPYING for details.
+
+"""
+Forms definition
+"""
+import datetime
+import re
+from itertools import groupby
+
+from django.core.urlresolvers import reverse
+from django.core import validators
+from django.core.exceptions import ObjectDoesNotExist
+from django.utils import formats
+from django.utils.functional import lazy
+from django.utils.translation import ugettext_lazy as _
+from django.shortcuts import render_to_response
+from django.template import Context, RequestContext, loader
+from django.db.models import Max
+from django import forms
+from django.core.mail import send_mail
+from django.forms.formsets import formset_factory, BaseFormSet, \
+ DELETION_FIELD_NAME
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+
+from formwizard.forms import NamedUrlSessionFormWizard
+
+import models
+import widgets
+from ishtar import settings
+
+reverse_lazy = lazy(reverse, unicode)
+
+regexp_name = re.compile(r'^[\w\- ]+$', re.UNICODE)
+name_validator = validators.RegexValidator(regexp_name,
+_(u"Enter a valid name consisting of letters, spaces and hyphens."), 'invalid')
+
+class FloatField(forms.FloatField):
+ """
+ Allow the use of comma for separating float fields
+ """
+ def clean(self, value):
+ if value:
+ value = value.replace(',', '.').replace('%', '')
+ return super(FloatField, self).clean(value)
+
+class FinalForm(forms.Form):
+ final = True
+ form_label = _(u"Confirm")
+
+class FormSet(BaseFormSet):
+ def check_duplicate(self, key_names, error_msg=""):
+ """Check for duplicate items in the formset"""
+ if any(self.errors):
+ return
+ if not error_msg:
+ error_msg = _("There are identical items.")
+ items = []
+ for i in range(0, self.total_form_count()):
+ form = self.forms[i]
+ if not form.is_valid():
+ continue
+ item = [key_name in form.cleaned_data and form.cleaned_data[key_name]
+ for key_name in key_names]
+ if not [v for v in item if v]:
+ continue
+ if item in items:
+ raise forms.ValidationError, error_msg
+ items.append(item)
+
+ def add_fields(self, form, index):
+ super(FormSet, self).add_fields(form, index)
+ form.fields[DELETION_FIELD_NAME].label = ''
+ form.fields[DELETION_FIELD_NAME].widget = widgets.DeleteWidget()
+
+class SearchWizard(NamedUrlSessionFormWizard):
+ model = None
+
+ def get_wizard_name(self):
+ """
+ As the class name can interfere when reused, use the url_name
+ """
+ return self.url_name
+
+ def get_template(self, request, storage):
+ templates = ['search.html']
+ return templates
+
+class Wizard(NamedUrlSessionFormWizard):
+ model = None
+
+ def get_wizard_name(self):
+ """
+ As the class name can interfere when reused, use the url_name
+ """
+ return self.url_name
+
+ def get_template(self, request, storage):
+ templates = ['default_wizard.html']
+ current_step = storage.get_current_step() or self.get_first_step(
+ request, storage)
+ if current_step == self.get_last_step(request, storage):
+ templates = ['confirm_wizard.html'] + templates
+ return templates
+
+ def get_template_context(self, request, storage, form=None):
+ """
+ Add previous and current steps to manage the wizard path
+ """
+ context = super(Wizard, self).get_template_context(request, storage,
+ form)
+ step = self.get_first_step(request, storage)
+ current_step = storage.get_current_step() or self.get_first_step(
+ request, storage)
+ context.update({'current_step':self.form_list[current_step]})
+ if step == current_step:
+ return context
+ previous_steps = []
+ while step:
+ if step == current_step:
+ break
+ previous_steps.append(self.form_list[step])
+ step = self.get_next_step(request, storage, step)
+ context.update({'previous_steps':previous_steps})
+ # not last step: validation
+ if step != self.get_last_step(request, storage):
+ return context
+ final_form_list = []
+ for form_key in self.get_form_list(request, storage).keys():
+ form_obj = self.get_form(request, storage, step=form_key,
+ data=storage.get_step_data(form_key),
+ files=storage.get_step_files(form_key))
+ form_obj.is_valid()
+ final_form_list.append(form_obj)
+ last_form = final_form_list[-1]
+ context.update({'datas':self.get_formated_datas(final_form_list)})
+ if hasattr(last_form, 'confirm_msg'):
+ context.update({'confirm_msg':last_form.confirm_msg})
+ if hasattr(last_form, 'confirm_end_msg'):
+ context.update({'confirm_end_msg':last_form.confirm_end_msg})
+ return context
+
+ def get_formated_datas(self, forms):
+ """
+ Get the data to present in the last page
+ """
+ datas = []
+ for form in forms:
+ form_datas = []
+ base_form = hasattr(form, 'forms') and form.forms[0] or form
+ associated_models = hasattr(base_form, 'associated_models') and \
+ base_form.associated_models or {}
+ if not hasattr(form, 'cleaned_data') and hasattr(form, 'forms'):
+ cleaned_datas = [frm.cleaned_data for frm in form.forms
+ if frm.is_valid()]
+ if not cleaned_datas:
+ continue
+ elif not hasattr(form, 'cleaned_data'):
+ continue
+ else:
+ cleaned_datas = type(form.cleaned_data) == list and \
+ form.cleaned_data \
+ or [form.cleaned_data]
+ for cleaned_data in cleaned_datas:
+ if not cleaned_data:
+ continue
+ if form_datas:
+ form_datas.append(("", "", "spacer"))
+ items = hasattr(base_form, 'fields') and \
+ base_form.fields.keyOrder or cleaned_data.keys()
+ for key in items:
+ lbl = None
+ if key.startswith('hidden_'):
+ continue
+ if hasattr(base_form, 'fields') and key in base_form.fields:
+ lbl = base_form.fields[key].label
+ if not lbl:
+ continue
+ value = cleaned_data[key]
+ if not value and value != False:
+ continue
+ if type(value) == bool:
+ if value == True:
+ value = _("Yes")
+ elif value == False:
+ value = _("No")
+ elif key in associated_models:
+ item = associated_models[key].objects.get(pk=value)
+ if hasattr(item, 'short_label'):
+ value = item.short_label()
+ else:
+ value = unicode(item)
+ form_datas.append((lbl, value, ''))
+ if form_datas:
+ datas.append((form.form_label, form_datas))
+ return datas
+
+ def get_extra_model(self, dct, request, storage, form_list):
+ dct['history_modifier'] = request.user
+ return dct
+
+ def done(self, request, storage, form_list, return_object=False, **kwargs):
+ """
+ Save to the model
+ """
+ dct, m2m, whole_associated_models = {}, [], []
+ for form in form_list:
+ if not form.is_valid():
+ return self.render(request, storage, form)
+ base_form = hasattr(form, 'forms') and form.forms[0] or form
+ associated_models = hasattr(base_form, 'associated_models') and \
+ base_form.associated_models or {}
+ if hasattr(form, 'forms'):
+ multi = False
+ if form.forms:
+ frm = form.forms[0]
+ if hasattr(frm, 'base_model') and frm.base_model:
+ whole_associated_models.append(frm.base_model)
+ else:
+ whole_associated_models += associated_models.keys()
+ fields = frm.fields.copy()
+ if 'DELETE' in fields:
+ fields.pop('DELETE')
+ multi = len(fields) > 1
+ if multi:
+ assert hasattr(frm, 'base_model'), \
+ u"Must define a base_model for " + unicode(frm.__class__)
+ for frm in form.forms:
+ if not frm.is_valid():
+ continue
+ vals = {}
+ if "DELETE" in frm.cleaned_data:
+ if frm.cleaned_data["DELETE"]:
+ continue
+ frm.cleaned_data.pop('DELETE')
+ for key in frm.cleaned_data:
+ value = frm.cleaned_data[key]
+ if not value and value != False:
+ continue
+ if key in associated_models:
+ value = associated_models[key].objects.get(pk=value)
+ if multi:
+ vals[key] = value
+ else:
+ m2m.append((key, value))
+ if multi and vals:
+ m2m.append((frm.base_model, vals))
+ elif type(form.cleaned_data) == dict:
+ for key in form.cleaned_data:
+ if key.startswith('hidden_'):
+ continue
+ value = form.cleaned_data[key]
+ if key in associated_models:
+ if value:
+ value = associated_models[key].objects.get(pk=value)
+ else:
+ value = None
+ dct[key] = value
+ return self.save_model(dct, m2m, whole_associated_models, request,
+ storage, form_list, return_object)
+
+ def get_saved_model(self):
+ """
+ Permit a distinguo when saved model is not the base selected model
+ """
+ return self.model
+
+ def get_current_saved_object(self, request, storage):
+ """
+ Permit a distinguo when saved model is not the base selected model
+ """
+ return self.get_current_object(request, storage)
+
+ def save_model(self, dct, m2m, whole_associated_models, request, storage,
+ form_list, return_object):
+ dct = self.get_extra_model(dct, request, storage, form_list)
+ obj = self.get_current_saved_object(request, storage)
+
+ # manage dependant items
+ other_objs = {}
+ for k in dct.keys():
+ if '__' not in k:
+ continue
+ vals = k.split('__')
+ assert len(vals) == 2, "Only one level of dependant item is managed"
+ dependant_item, key = vals
+ if dependant_item not in other_objs:
+ other_objs[dependant_item] = {}
+ other_objs[dependant_item][key] = dct.pop(k)
+ if obj:
+ for k in dct:
+ if k.startswith('pk'):
+ continue
+ setattr(obj, k, dct[k])
+ for dependant_item in other_objs:
+ c_item = getattr(obj, dependant_item)
+ # manage ManyToMany if only one associated
+ if hasattr(c_item, "all"):
+ c_items = c_item.all()
+ if len(c_items) != 1:
+ continue
+ c_item = c_items[0]
+ if c_item:
+ # to check #
+ for k in other_objs[dependant_item]:
+ setattr(c_item, k, other_objs[dependant_item][k])
+ c_item.save()
+ else:
+ m = getattr(self.model, dependant_item)
+ if hasattr(m, 'related'):
+ c_item = m.related.model(**other_objs[dependant_item])
+ setattr(obj, dependant_item, c_item)
+ obj.save()
+ obj.save()
+ else:
+ adds = {}
+ for dependant_item in other_objs:
+ m = getattr(self.model, dependant_item)
+ model = m.field.rel.to
+ c_dct = other_objs[dependant_item].copy()
+ if hasattr(model, 'history'):
+ c_dct['history_modifier'] = request.user
+ c_item = model(**c_dct)
+ c_item.save()
+ if hasattr(m, 'through'):
+ adds[dependant_item] = c_item
+ elif hasattr(m, 'field'):
+ dct[dependant_item] = c_item
+ if 'pk' in dct:
+ dct.pop('pk')
+ obj = self.get_saved_model()(**dct)
+ obj.save()
+ for k in adds:
+ getattr(obj, k).add(adds[k])
+ # necessary to manage interaction between models like
+ # material_index management for baseitems
+ obj.save()
+ m2m_items = {}
+ for model in whole_associated_models:
+ getattr(obj, model+'s').clear()
+ for key, value in m2m:
+ if key not in m2m_items:
+ if type(key) == dict:
+ vals = []
+ for item in getattr(obj, key+'s').all():
+ v = {}
+ for k in value.keys():
+ v[k] = getattr(item, k)
+ vals.append(v)
+ m2m_items[key] = vals
+ else:
+ m2m_items[key] = getattr(obj, key+'s').all()
+ if value not in m2m_items[key]:
+ if type(value) == dict:
+ model = getattr(obj, key+'s').model
+ if issubclass(model, models.BaseHistorizedItem):
+ value['history_modifier'] = request.user
+ value = model.objects.create(**value)
+ value.save()
+ getattr(obj, key+'s').add(value)
+ # necessary to manage interaction between models like
+ # material_index management for baseitems
+ obj.save()
+ res = render_to_response('wizard_done.html', {},
+ context_instance=RequestContext(request))
+ return return_object and (obj, res) or res
+
+ def get_deleted(self, keys):
+ """
+ Get the deleted and non-deleted items in formsets
+ """
+ not_to_delete, to_delete = set(), set()
+ for key in keys:
+ items = key.split('-')
+ if len(items) < 2 or items[-2] in to_delete:
+ continue
+ idx = items[-2]
+ try:
+ int(idx)
+ except:
+ continue
+ if items[-1] == u'DELETE':
+ to_delete.add(idx)
+ if idx in not_to_delete:
+ not_to_delete.remove(idx)
+ elif idx not in not_to_delete:
+ not_to_delete.add(idx)
+ return (to_delete, not_to_delete)
+
+ def get_form(self, request, storage, step=None, data=None, files=None):
+ """
+ Manage formset
+ """
+ if data:
+ data = data.copy()
+ if not step:
+ step = self.determine_step(request, storage)
+ form = self.get_form_list(request, storage)[step]
+ if hasattr(form, 'management_form'):
+ # manage deletion
+ to_delete, not_to_delete = self.get_deleted(data.keys())
+ # raz deleted fields
+ for key in data.keys():
+ items = key.split('-')
+ if len(items) < 2 or items[-2] not in to_delete:
+ continue
+ data.pop(key)
+ if to_delete:
+ # reorganize
+ for idx, number in enumerate(sorted(not_to_delete)):
+ idx = unicode(idx)
+ if idx == number:
+ continue
+ for key in data.keys():
+ items = key.split('-')
+ if len(items) > 2 and number == items[-2]:
+ items[-2] = unicode(idx)
+ k = u'-'.join(items)
+ data[k] = data.pop(key)[0]
+ # get a form key
+ base_key = form.form.base_fields.keys()[0]
+ init = self.get_form_initial(request, storage, step)
+ total_field = len([key for key in data.keys()
+ if base_key in key.split('-')
+ and data[key]])
+ if init and not to_delete:
+ total_field = max((total_field, len(init)))
+ data[step + u'-INITIAL_FORMS'] = unicode(total_field)
+ data[step + u'-TOTAL_FORMS'] = unicode(total_field + 1)
+ data = data or None
+ form = super(Wizard, self).get_form(request, storage, step, data, files)
+ return form
+
+ def render_next_step(self, request, storage, form, **kwargs):
+ """
+ Manage the modify or delete button in formset: next_step = current_step
+ """
+ if request.POST.has_key('formset_modify') \
+ and request.POST['formset_modify'] \
+ or [key for key in request.POST.keys()
+ if key.endswith('DELETE') and request.POST[key]]:
+ return self.render(request, storage, form, **kwargs)
+ return super(Wizard, self).render_next_step(request, storage, form,
+ **kwargs)
+
+ def process_post_request(self, request, storage, *args, **kwargs):
+ """
+ Convert numerical step number to step name
+ """
+ post_data = request.POST.copy()
+ if request.POST.has_key('form_prev_step'):
+ try:
+ step_number = int(request.POST['form_prev_step'])
+ post_data['form_prev_step'] = self.get_form_list(request,
+ storage).keys()[step_number]
+ except ValueError:
+ pass
+ request.POST = post_data
+ return super(Wizard, self).process_post_request(request, storage, *args,
+ **kwargs)
+ @classmethod
+ def session_has_key(cls, request, storage, form_key, key=None, multi=None):
+ """
+ Check if the session has value of a specific form and (if provided)
+ of a key
+ """
+ test = storage.prefix in request.session \
+ and 'step_data' in request.session[storage.prefix] \
+ and form_key in request.session[storage.prefix]['step_data']
+ if not key or not test:
+ return test
+ key = key.startswith(form_key) and key or \
+ not multi and form_key + '-' + key or \
+ form_key + '-0-' + key #only check if the first field is available
+ return key in request.session[storage.prefix]['step_data'][form_key]
+
+ @classmethod
+ def session_get_value(cls, request, storage, form_key, key, multi=False):
+ """
+ Get the value of a specific form
+ """
+ if not cls.session_has_key(request, storage, form_key, key, multi):
+ return
+ if not multi:
+ key = key.startswith(form_key) and key or form_key + '-' + key
+ return request.session[storage.prefix]['step_data'][form_key][key]
+ vals = []
+ for k in request.session[storage.prefix]['step_data'][form_key]:
+ if k.startswith(form_key) and k.endswith(key) and \
+ request.session[storage.prefix]['step_data'][form_key][k]:
+ vals.append(request.session[storage.prefix]['step_data']\
+ [form_key][k])
+ return vals
+
+ def get_current_object(self, request, storage):
+ """
+ Get the current object for an instancied wizard
+ """
+ current_obj = None
+ main_form_key = 'selec-' + self.url_name
+ try:
+ idx = int(self.session_get_value(request, storage, main_form_key,
+ 'pk'))
+ current_obj = self.model.objects.get(pk=idx)
+ except(TypeError, ValueError, ObjectDoesNotExist):
+ pass
+ return current_obj
+
+ def get_form_initial(self, request, storage, step):
+ current_obj = self.get_current_object(request, storage)
+ current_step = storage.get_current_step() or self.get_first_step(
+ request, storage)
+ if step.startswith('selec-') and step in self.form_list \
+ and 'pk' in self.form_list[step].associated_models:
+ model_name = self.form_list[step].associated_models['pk'
+ ].__name__.lower()
+ if step == current_step:
+ self.reset_wizard(request, storage)
+ val = model_name in request.session and request.session[model_name]
+ if val:
+ return {'pk':val}
+ elif current_obj:
+ return self.get_instanced_init(current_obj, request, storage,
+ step)
+ current_form = self.form_list[current_step]
+ if hasattr(current_form, 'currents'):
+ initial = {}
+ for key in current_form.currents:
+ model_name = current_form.currents[key].__name__.lower()
+ val = model_name in request.session and \
+ request.session[model_name]
+ if val:
+ initial[key] = val
+ if initial:
+ return initial
+ return super(Wizard, self).get_form_initial(request, storage, step)
+
+ def get_instanced_init(self, obj, request, storage, step):
+ """
+ Get initial data from an init
+ """
+ current_step = storage.get_current_step() or self.get_first_step(
+ request, storage)
+ c_form = self.form_list[current_step]
+ # make the current object the default item for the session
+ obj_name = obj.__class__.__name__.lower()
+ # prefer a specialized name if available
+ prefixes = storage.prefix.split('_')
+ if len(prefixes) > 1 and prefixes[-2].startswith(obj_name):
+ obj_name = prefixes[-2]
+ request.session[obj_name] = unicode(obj.pk)
+ initial = {}
+ if request.POST or (step in request.session[storage.prefix] and\
+ request.session[storage.prefix]['step_data'][step]):
+ return {}
+ if hasattr(c_form, 'base_fields'):
+ for base_field in c_form.base_fields.keys():
+ fields = base_field.split('__')
+ value = obj
+ for field in fields:
+ if not hasattr(value, field) or \
+ getattr(value, field) == None:
+ value = obj
+ break
+ value = getattr(value, field)
+ if value == obj:
+ continue
+ if hasattr(value, 'pk'):
+ value = value.pk
+ if value in (True, False):
+ initial[base_field] = value
+ elif value != None:
+ initial[base_field] = unicode(value)
+ elif hasattr(c_form, 'management_form'):
+ initial = []
+ key = current_step.split('-')[0]
+ if not hasattr(obj, key):
+ return initial
+ keys = c_form.form.base_fields.keys()
+ for child_obj in getattr(obj, key).order_by('pk').all():
+ if not keys:
+ break
+ vals = {}
+ if len(keys) == 1:
+ # only one field: must be the id of the object
+ vals[keys[0]] = unicode(child_obj.pk)
+ else:
+ for field in keys:
+ if hasattr(child_obj, field):
+ value = getattr(child_obj, field)
+ if hasattr(value, 'pk'):
+ value = value.pk
+ if value != None:
+ vals[field] = unicode(value)
+ if vals:
+ initial.append(vals)
+ return initial
+
+def get_now():
+ format = formats.get_format('DATE_INPUT_FORMATS')[0]
+ value = datetime.datetime.now().strftime(format)
+ return value
+
+class DeletionWizard(Wizard):
+ def get_formated_datas(self, forms):
+ datas = super(DeletionWizard, self).get_formated_datas(forms)
+ self.current_obj = None
+ for form in forms:
+ if not hasattr(form, "cleaned_data"):
+ continue
+ for key in form.cleaned_data:
+ if key == 'pk':
+ model = form.associated_models['pk']
+ self.current_obj = model.objects.get(pk=form.cleaned_data['pk'])
+ if not self.current_obj:
+ return datas
+ res = {}
+ for field in self.model._meta.fields + self.model._meta.many_to_many:
+ if field.name not in self.fields:
+ continue
+ value = getattr(self.current_obj, field.name)
+ if not value:
+ continue
+ if hasattr(value, 'all'):
+ value = ", ".join([unicode(item) for item in value.all()])
+ if not value:
+ continue
+ else:
+ value = unicode(value)
+ res[field.name] = (field.verbose_name, value, '')
+ if not datas and self.fields:
+ datas = [['', []]]
+ for field in self.fields:
+ if field in res:
+ datas[0][1].append(res[field])
+ return datas
+
+ def done(self, request, storage, form_list, **kwargs):
+ obj = self.get_current_object(request, storage)
+ obj.delete()
+ return render_to_response('wizard_delete_done.html', {},
+ context_instance=RequestContext(request))
+
+class ClosingWizard(Wizard):
+ # "close" an item
+ # to be define in the overloaded class
+ model = None
+ fields = []
+
+ def get_formated_datas(self, forms):
+ datas = super(ClosingWizard, self).get_formated_datas(forms)
+ self.current_obj = None
+ for form in forms:
+ if not hasattr(form, "cleaned_data"):
+ continue
+ for key in form.cleaned_data:
+ if key == 'pk':
+ model = form.associated_models['pk']
+ self.current_obj = model.objects.get(
+ pk=form.cleaned_data['pk'])
+ if not self.current_obj:
+ return datas
+ res = {}
+ for field in self.model._meta.fields + self.model._meta.many_to_many:
+ if field.name not in self.fields:
+ continue
+ value = getattr(self.current_obj, field.name)
+ if not value:
+ continue
+ if hasattr(value, 'all'):
+ value = ", ".join([unicode(item) for item in value.all()])
+ if not value:
+ continue
+ else:
+ value = unicode(value)
+ res[field.name] = (field.verbose_name, value, '')
+ if not datas and self.fields:
+ datas = [['', []]]
+ for field in self.fields:
+ if field in res:
+ datas[0][1].append(res[field])
+ return datas
+
+ def done(self, request, storage, form_list, **kwargs):
+ obj = self.get_current_object(request, storage)
+ for form in form_list:
+ if form.is_valid():
+ if 'end_date' in form.cleaned_data and hasattr(obj, 'end_date'):
+ obj.end_date = form.cleaned_data['end_date']
+ obj.save()
+ return render_to_response('wizard_closing_done.html', {},
+ context_instance=RequestContext(request))
+
+def get_form_selection(class_name, label, key, model, base_form, get_url,
+ not_selected_error=_(u"You should select an item.")):
+ """
+ Generate a class selection form
+ class_name -- name of the class
+ label -- label of the form
+ key -- model,
+ base_form -- base form to select
+ get_url -- url to get the item
+ not_selected_error -- message displayed when no item is selected
+ """
+ attrs = {'_main_key':key,
+ '_not_selected_error':not_selected_error,
+ 'form_label':label,
+ 'associated_models':{key:model},
+ 'currents':{key:model},}
+ attrs[key] = forms.IntegerField(label="", required=False,
+ widget=widgets.JQueryJqGrid(reverse_lazy(get_url),
+ base_form(), model), validators=[models.valid_id(model)])
+ def clean(self):
+ cleaned_data = self.cleaned_data
+ if self._main_key not in cleaned_data \
+ or not cleaned_data[self._main_key]:
+ raise forms.ValidationError(self._not_selected_error)
+ return cleaned_data
+ return type(class_name, (forms.Form,), attrs)