# -*- encodung=utf-8 -*- from collections import OrderedDict from django.core.exceptions import NON_FIELD_ERRORS from django.forms import BaseForm from django.forms.utils import ErrorList from django.utils.safestring import mark_safe from .prefixdict import PrefixCombiningDict class MultiForm(BaseForm): PREFIX_SEP = '-' VAR_PREFIX_SEP = '__' base_fields = {} def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, label_suffix=None, empty_permitted=False, **kwargs ): self.prefix_sep = self.PREFIX_SEP self.var_prefix_sep = self.VAR_PREFIX_SEP super(MultiForm, self).__init__(data=data, files=files, auto_id=auto_id, prefix=prefix, initial=initial, error_class=error_class, label_suffix=label_suffix, empty_permitted=empty_permitted ) self._init_subforms(data=data, initial=initial, label_suffix=label_suffix, **kwargs) self.fields = PrefixCombiningDict(((prefix, form.fields) for prefix, form in self.forms.iteritems() if form.fields), sep=self.prefix_sep) def _init_subforms(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, label_suffix=None, empty_permitted=False, **kwargs ): if not prefix: prefix = '' form_classes = self.get_form_classes() if len(form_classes) == 0: raise ValueError("Need at least one form class for MultiForm") forms = OrderedDict() for key, form_class in form_classes.iteritems(): sub_initial = initial.get(key) if initial else None sub_prefix = prefix + key sub_kwargs = {kw_key: kw_value[key] for kw_key, kw_value in kwargs.iteritems() if key in kw_value} forms[key] = self.create_form( form_class, data=data, files=files, auto_id=auto_id, prefix=sub_prefix, initial=sub_initial, error_class=error_class, label_suffix=label_suffix, empty_permitted=empty_permitted, **sub_kwargs ) self.forms = forms def create_form(self, form_class, data=None, files=None, auto_id=None, prefix=None, initial=None, error_class=None, label_suffix=None, empty_permitted=None, *args, **kwargs ): return form_class( data=data, files=files, auto_id=auto_id, prefix=prefix, initial=initial, error_class=error_class, label_suffix=label_suffix, empty_permitted=empty_permitted, *args, **kwargs ) def full_clean(self): for form in self.forms.itervalues(): if form: form.full_clean() self.cleaned_data = PrefixCombiningDict( ((prefix, form.clean()) for prefix, form in self.forms.iteritems() if form and form.is_valid()), sep=self.prefix_sep ) self._errors = {NON_FIELD_ERRORS: []} for prefix, form in (form for form in self.forms.iteritems() if form): for field, errors in form.errors.iteritems(): if field == NON_FIELD_ERRORS: self._errors[NON_FIELD_ERRORS].extend(errors) else: self._errors[prefix + self.prefix_sep + field] = errors return self.cleaned_data def get_form_classes(self): try: return self.form_classes except NameError: raise NameError('Subclasses must set form_classes or override get_form_classes()') def __unicode__(self): return mark_safe(u"\n".join(unicode(form) for form in self.forms.itervalues())) def __str__(self): return mark_safe("\n".join(str(form) for form in self.forms.itervalues())) def __iter___(self): for name in self.fields: yield self[name] def __getitem__(self, key): return PrefixCombiningDict(((prefix, form) for prefix, form in self.forms.iteritems() if form), sep=self.prefix_sep)[key] @property def varfields(self): return PrefixCombiningDict(((prefix, form) for prefix, form in self.forms.iteritems() if form), sep=self.var_prefix_sep) def is_valid(self): self.full_clean() return all(form.is_valid() for form in self.forms.itervalues()) class ModelMultiForm(MultiForm): def create_form(self, form_class, data=None, files=None, auto_id=None, prefix=None, initial=None, instance=None, error_class=None, label_suffix=None, empty_permitted=None, *args, **kwargs ): if instance: return form_class(data=data, prefix=prefix, initial=initial, instance=instance, *args, **kwargs) else: return form_class(data=data, prefix=prefix, initial=initial, *args, **kwargs) @property def instance(self): return dict((prefix, form.instance) for prefix, form in self.forms.iteritems() if form) def save(self, commit=True): return dict((key, form.save(commit=commit)) for key, form in self.forms.iteritems() if form)