1
from __future__ import absolute_import
1
from __future__ import absolute_import, unicode_literals
3
3
from django.core.exceptions import ValidationError
4
4
from django.forms import Form
5
5
from django.forms.fields import IntegerField, BooleanField
6
6
from django.forms.util import ErrorList
7
7
from django.forms.widgets import Media, HiddenInput
8
from django.utils.encoding import StrAndUnicode
8
from django.utils.encoding import python_2_unicode_compatible
9
9
from django.utils.safestring import mark_safe
10
from django.utils import six
11
from django.utils.six.moves import xrange
10
12
from django.utils.translation import ugettext as _
34
36
self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput)
35
37
super(ManagementForm, self).__init__(*args, **kwargs)
37
class BaseFormSet(StrAndUnicode):
39
@python_2_unicode_compatible
40
class BaseFormSet(object):
39
42
A collection of instances of the same Form class.
67
70
return len(self.forms)
69
def __nonzero__(self):
70
73
"""All formsets have a management form which is not included in the length"""
73
def _management_form(self):
76
def __nonzero__(self): # Python 2 compatibility
77
return type(self).__bool__(self)
80
def management_form(self):
74
81
"""Returns the ManagementForm instance for this FormSet."""
76
83
form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
122
128
Instantiates and returns the i-th form instance in a formset.
124
defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
131
'auto_id': self.auto_id,
132
'prefix': self.add_prefix(i),
133
'error_class': self.error_class,
125
135
if self.is_bound:
126
136
defaults['data'] = self.data
127
137
defaults['files'] = self.files
138
148
self.add_fields(form, i)
141
def _get_initial_forms(self):
152
def initial_forms(self):
142
153
"""Return a list of all the initial forms in this formset."""
143
154
return self.forms[:self.initial_form_count()]
144
initial_forms = property(_get_initial_forms)
146
def _get_extra_forms(self):
157
def extra_forms(self):
147
158
"""Return a list of all the extra forms in this formset."""
148
159
return self.forms[self.initial_form_count():]
149
extra_forms = property(_get_extra_forms)
151
def _get_empty_form(self, **kwargs):
153
'auto_id': self.auto_id,
154
'prefix': self.add_prefix('__prefix__'),
155
'empty_permitted': True,
157
defaults.update(kwargs)
158
form = self.form(**defaults)
162
def empty_form(self):
164
auto_id=self.auto_id,
165
prefix=self.add_prefix('__prefix__'),
166
empty_permitted=True,
159
168
self.add_fields(form, None)
161
empty_form = property(_get_empty_form)
163
171
# Maybe this should just go away?
164
def _get_cleaned_data(self):
173
def cleaned_data(self):
166
175
Returns a list of form.cleaned_data dicts for every form in self.forms.
168
177
if not self.is_valid():
169
178
raise AttributeError("'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__)
170
179
return [form.cleaned_data for form in self.forms]
171
cleaned_data = property(_get_cleaned_data)
173
def _get_deleted_forms(self):
182
def deleted_forms(self):
175
184
Returns a list of forms that have been marked for deletion. Raises an
176
185
AttributeError if deletion is not allowed.
189
198
if self._should_delete_form(form):
190
199
self._deleted_form_indexes.append(i)
191
200
return [self.forms[i] for i in self._deleted_form_indexes]
192
deleted_forms = property(_get_deleted_forms)
194
def _get_ordered_forms(self):
203
def ordered_forms(self):
196
205
Returns a list of form in the order specified by the incoming data.
197
206
Raises an AttributeError if ordering is not allowed.
242
250
return self._non_form_errors
243
251
return self.error_class()
245
def _get_errors(self):
247
256
Returns a list of form.errors for every form in self.forms.
249
258
if self._errors is None:
250
259
self.full_clean()
251
260
return self._errors
252
errors = property(_get_errors)
254
262
def _should_delete_form(self, form):
255
# The way we lookup the value of the deletion field here takes
256
# more code than we'd like, but the form's cleaned_data will
257
# not exist if the form is invalid.
258
field = form.fields[DELETION_FIELD_NAME]
259
raw_value = form._raw_value(DELETION_FIELD_NAME)
260
should_delete = field.clean(raw_value)
264
Returns whether or not the form was marked for deletion.
266
return form.cleaned_data.get(DELETION_FIELD_NAME, False)
263
268
def is_valid(self):
265
Returns True if form.errors is empty for every form in self.forms.
270
Returns True if every form in self.forms is valid.
267
272
if not self.is_bound:
294
298
# Give self.clean() a chance to do cross-form validation.
297
except ValidationError, e:
301
except ValidationError as e:
298
302
self._non_form_errors = self.error_class(e.messages)
317
321
if self.can_order:
318
322
# Only pre-fill the ordering field for initial forms.
319
323
if index is not None and index < self.initial_form_count():
320
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False)
324
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), initial=index+1, required=False)
322
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False)
326
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), required=False)
323
327
if self.can_delete:
324
form.fields[DELETION_FIELD_NAME] = BooleanField(label=_(u'Delete'), required=False)
328
form.fields[DELETION_FIELD_NAME] = BooleanField(label=_('Delete'), required=False)
326
330
def add_prefix(self, index):
327
331
return '%s-%s' % (self.prefix, index)
331
335
Returns True if the formset needs to be multipart, i.e. it
332
336
has FileInput. Otherwise, False.
334
return self.forms and self.forms[0].is_multipart()
339
return self.forms[0].is_multipart()
341
return self.empty_form.is_multipart()
336
def _get_media(self):
337
345
# All the forms on a FormSet are the same, so you only need to
338
346
# interrogate the first form for media.
340
348
return self.forms[0].media
343
media = property(_get_media)
350
return self.empty_form.media
345
352
def as_table(self):
346
353
"Returns this formset rendered as HTML <tr>s -- excluding the <table></table>."
347
354
# XXX: there is no semantic division between forms here, there
348
355
# probably should be. It might make sense to render each form as a
349
356
# table row with each field as a td.
350
forms = u' '.join([form.as_table() for form in self])
351
return mark_safe(u'\n'.join([unicode(self.management_form), forms]))
357
forms = ' '.join([form.as_table() for form in self])
358
return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
354
361
"Returns this formset rendered as HTML <p>s."
355
forms = u' '.join([form.as_p() for form in self])
356
return mark_safe(u'\n'.join([unicode(self.management_form), forms]))
362
forms = ' '.join([form.as_p() for form in self])
363
return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
359
366
"Returns this formset rendered as HTML <li>s."
360
forms = u' '.join([form.as_ul() for form in self])
361
return mark_safe(u'\n'.join([unicode(self.management_form), forms]))
367
forms = ' '.join([form.as_ul() for form in self])
368
return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
363
370
def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
364
371
can_delete=False, max_num=None):
371
378
attrs = {'form': form, 'extra': extra,
372
379
'can_order': can_order, 'can_delete': can_delete,
373
380
'max_num': max_num, 'absolute_max': absolute_max}
374
return type(form.__name__ + 'FormSet', (formset,), attrs)
381
return type(form.__name__ + str('FormSet'), (formset,), attrs)
376
383
def all_valid(formsets):
377
384
"""Returns true if every formset in formsets is valid."""