Login

Another Multiform

Author:
dhke
Posted:
August 27, 2014
Language:
Python
Version:
1.6
Tags:
models forms multiform
Score:
0 (after 0 ratings)

MultiForm and MultiModelForm

Based on a PrefixDict class I wrote and thus very lean. Lacks a little documentation, though

class MyMultiForm(ModelMultiForm):
    class Meta:
        localized_fields = '__all__'

    form_classes = OrderedDict((
        ('form1', Form1),
        ('form2', Form2),
    ))

Subfields are named form-name prefix_sep subfield-name. prefix_sep defaults to -. For access in templates, use form.varfields, which uses var_prefix_sep (default: _), instead.

multiform.varfields()['form1_field1']
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# -*- 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)

More like this

  1. Form rendering using a template instead of builtin HTML by leoh 9 years, 1 month ago
  2. Username form field by sma 7 years, 8 months ago
  3. Generic model filter from request GET data by genbit 5 years ago
  4. Imagefield with variations by fivethreeo 7 years, 11 months ago
  5. Template filter to markup form fields with optional args by fleggs 4 years, 7 months ago

Comments

luzfcb (on October 24, 2014):

create a simple example of how to use it

#

Please login first before commenting.