summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/django/forms/formsets.py
diff options
context:
space:
mode:
authorttt2017-05-13 00:29:47 +0530
committerttt2017-05-13 00:29:47 +0530
commit4336f5f06f61de30ae3fa54650fce63a9d5ef5be (patch)
tree23b4ee9b8e8f24bf732acf2f7ad22ed50cdd5670 /lib/python2.7/site-packages/django/forms/formsets.py
downloadSBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.tar.gz
SBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.tar.bz2
SBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.zip
added all server files
Diffstat (limited to 'lib/python2.7/site-packages/django/forms/formsets.py')
-rw-r--r--lib/python2.7/site-packages/django/forms/formsets.py417
1 files changed, 417 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/forms/formsets.py b/lib/python2.7/site-packages/django/forms/formsets.py
new file mode 100644
index 0000000..cb3126e
--- /dev/null
+++ b/lib/python2.7/site-packages/django/forms/formsets.py
@@ -0,0 +1,417 @@
+from __future__ import absolute_import, unicode_literals
+
+from django.core.exceptions import ValidationError
+from django.forms import Form
+from django.forms.fields import IntegerField, BooleanField
+from django.forms.util import ErrorList
+from django.forms.widgets import HiddenInput
+from django.utils.encoding import python_2_unicode_compatible
+from django.utils.functional import cached_property
+from django.utils.safestring import mark_safe
+from django.utils import six
+from django.utils.six.moves import xrange
+from django.utils.translation import ungettext, ugettext as _
+
+
+__all__ = ('BaseFormSet', 'all_valid')
+
+# special field names
+TOTAL_FORM_COUNT = 'TOTAL_FORMS'
+INITIAL_FORM_COUNT = 'INITIAL_FORMS'
+MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS'
+ORDERING_FIELD_NAME = 'ORDER'
+DELETION_FIELD_NAME = 'DELETE'
+
+# default maximum number of forms in a formset, to prevent memory exhaustion
+DEFAULT_MAX_NUM = 1000
+
+class ManagementForm(Form):
+ """
+ ``ManagementForm`` is used to keep track of how many form instances
+ are displayed on the page. If adding new forms via javascript, you should
+ increment the count field of this form as well.
+ """
+ def __init__(self, *args, **kwargs):
+ self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
+ self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
+ # MAX_NUM_FORM_COUNT is output with the rest of the management form,
+ # but only for the convenience of client-side code. The POST
+ # value of MAX_NUM_FORM_COUNT returned from the client is not checked.
+ self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput)
+ super(ManagementForm, self).__init__(*args, **kwargs)
+
+@python_2_unicode_compatible
+class BaseFormSet(object):
+ """
+ A collection of instances of the same Form class.
+ """
+ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
+ initial=None, error_class=ErrorList):
+ self.is_bound = data is not None or files is not None
+ self.prefix = prefix or self.get_default_prefix()
+ self.auto_id = auto_id
+ self.data = data or {}
+ self.files = files or {}
+ self.initial = initial
+ self.error_class = error_class
+ self._errors = None
+ self._non_form_errors = None
+
+ def __str__(self):
+ return self.as_table()
+
+ def __iter__(self):
+ """Yields the forms in the order they should be rendered"""
+ return iter(self.forms)
+
+ def __getitem__(self, index):
+ """Returns the form at the given index, based on the rendering order"""
+ return self.forms[index]
+
+ def __len__(self):
+ return len(self.forms)
+
+ def __bool__(self):
+ """All formsets have a management form which is not included in the length"""
+ return True
+
+ def __nonzero__(self): # Python 2 compatibility
+ return type(self).__bool__(self)
+
+ @property
+ def management_form(self):
+ """Returns the ManagementForm instance for this FormSet."""
+ if self.is_bound:
+ form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
+ if not form.is_valid():
+ raise ValidationError(
+ _('ManagementForm data is missing or has been tampered with'),
+ code='missing_management_form',
+ )
+ else:
+ form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={
+ TOTAL_FORM_COUNT: self.total_form_count(),
+ INITIAL_FORM_COUNT: self.initial_form_count(),
+ MAX_NUM_FORM_COUNT: self.max_num
+ })
+ return form
+
+ def total_form_count(self):
+ """Returns the total number of forms in this FormSet."""
+ if self.is_bound:
+ # return absolute_max if it is lower than the actual total form
+ # count in the data; this is DoS protection to prevent clients
+ # from forcing the server to instantiate arbitrary numbers of
+ # forms
+ return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)
+ else:
+ initial_forms = self.initial_form_count()
+ total_forms = initial_forms + self.extra
+ # Allow all existing related objects/inlines to be displayed,
+ # but don't allow extra beyond max_num.
+ if initial_forms > self.max_num >= 0:
+ total_forms = initial_forms
+ elif total_forms > self.max_num >= 0:
+ total_forms = self.max_num
+ return total_forms
+
+ def initial_form_count(self):
+ """Returns the number of forms that are required in this FormSet."""
+ if self.is_bound:
+ return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
+ else:
+ # Use the length of the initial data if it's there, 0 otherwise.
+ initial_forms = len(self.initial) if self.initial else 0
+ return initial_forms
+
+ @cached_property
+ def forms(self):
+ """
+ Instantiate forms at first property access.
+ """
+ # DoS protection is included in total_form_count()
+ forms = [self._construct_form(i) for i in xrange(self.total_form_count())]
+ return forms
+
+ def _construct_form(self, i, **kwargs):
+ """
+ Instantiates and returns the i-th form instance in a formset.
+ """
+ defaults = {
+ 'auto_id': self.auto_id,
+ 'prefix': self.add_prefix(i),
+ 'error_class': self.error_class,
+ }
+ if self.is_bound:
+ defaults['data'] = self.data
+ defaults['files'] = self.files
+ if self.initial and not 'initial' in kwargs:
+ try:
+ defaults['initial'] = self.initial[i]
+ except IndexError:
+ pass
+ # Allow extra forms to be empty.
+ if i >= self.initial_form_count():
+ defaults['empty_permitted'] = True
+ defaults.update(kwargs)
+ form = self.form(**defaults)
+ self.add_fields(form, i)
+ return form
+
+ @property
+ def initial_forms(self):
+ """Return a list of all the initial forms in this formset."""
+ return self.forms[:self.initial_form_count()]
+
+ @property
+ def extra_forms(self):
+ """Return a list of all the extra forms in this formset."""
+ return self.forms[self.initial_form_count():]
+
+ @property
+ def empty_form(self):
+ form = self.form(
+ auto_id=self.auto_id,
+ prefix=self.add_prefix('__prefix__'),
+ empty_permitted=True,
+ )
+ self.add_fields(form, None)
+ return form
+
+ @property
+ def cleaned_data(self):
+ """
+ Returns a list of form.cleaned_data dicts for every form in self.forms.
+ """
+ if not self.is_valid():
+ raise AttributeError("'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__)
+ return [form.cleaned_data for form in self.forms]
+
+ @property
+ def deleted_forms(self):
+ """
+ Returns a list of forms that have been marked for deletion.
+ """
+ if not self.is_valid() or not self.can_delete:
+ return []
+ # construct _deleted_form_indexes which is just a list of form indexes
+ # that have had their deletion widget set to True
+ if not hasattr(self, '_deleted_form_indexes'):
+ self._deleted_form_indexes = []
+ for i in range(0, self.total_form_count()):
+ form = self.forms[i]
+ # if this is an extra form and hasn't changed, don't consider it
+ if i >= self.initial_form_count() and not form.has_changed():
+ continue
+ if self._should_delete_form(form):
+ self._deleted_form_indexes.append(i)
+ return [self.forms[i] for i in self._deleted_form_indexes]
+
+ @property
+ def ordered_forms(self):
+ """
+ Returns a list of form in the order specified by the incoming data.
+ Raises an AttributeError if ordering is not allowed.
+ """
+ if not self.is_valid() or not self.can_order:
+ raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__)
+ # Construct _ordering, which is a list of (form_index, order_field_value)
+ # tuples. After constructing this list, we'll sort it by order_field_value
+ # so we have a way to get to the form indexes in the order specified
+ # by the form data.
+ if not hasattr(self, '_ordering'):
+ self._ordering = []
+ for i in range(0, self.total_form_count()):
+ form = self.forms[i]
+ # if this is an extra form and hasn't changed, don't consider it
+ if i >= self.initial_form_count() and not form.has_changed():
+ continue
+ # don't add data marked for deletion to self.ordered_data
+ if self.can_delete and self._should_delete_form(form):
+ continue
+ self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME]))
+ # After we're done populating self._ordering, sort it.
+ # A sort function to order things numerically ascending, but
+ # None should be sorted below anything else. Allowing None as
+ # a comparison value makes it so we can leave ordering fields
+ # blank.
+ def compare_ordering_key(k):
+ if k[1] is None:
+ return (1, 0) # +infinity, larger than any number
+ return (0, k[1])
+ self._ordering.sort(key=compare_ordering_key)
+ # Return a list of form.cleaned_data dicts in the order specified by
+ # the form data.
+ return [self.forms[i[0]] for i in self._ordering]
+
+ @classmethod
+ def get_default_prefix(cls):
+ return 'form'
+
+ def non_form_errors(self):
+ """
+ Returns an ErrorList of errors that aren't associated with a particular
+ form -- i.e., from formset.clean(). Returns an empty ErrorList if there
+ are none.
+ """
+ if self._non_form_errors is None:
+ self.full_clean()
+ return self._non_form_errors
+
+ @property
+ def errors(self):
+ """
+ Returns a list of form.errors for every form in self.forms.
+ """
+ if self._errors is None:
+ self.full_clean()
+ return self._errors
+
+ def total_error_count(self):
+ """
+ Returns the number of errors across all forms in the formset.
+ """
+ return len(self.non_form_errors()) +\
+ sum(len(form_errors) for form_errors in self.errors)
+
+ def _should_delete_form(self, form):
+ """
+ Returns whether or not the form was marked for deletion.
+ """
+ return form.cleaned_data.get(DELETION_FIELD_NAME, False)
+
+ def is_valid(self):
+ """
+ Returns True if every form in self.forms is valid.
+ """
+ if not self.is_bound:
+ return False
+ # We loop over every form.errors here rather than short circuiting on the
+ # first failure to make sure validation gets triggered for every form.
+ forms_valid = True
+ err = self.errors
+ for i in range(0, self.total_form_count()):
+ form = self.forms[i]
+ if self.can_delete:
+ if self._should_delete_form(form):
+ # This form is going to be deleted so any of its errors
+ # should not cause the entire formset to be invalid.
+ continue
+ forms_valid &= form.is_valid()
+ return forms_valid and not bool(self.non_form_errors())
+
+ def full_clean(self):
+ """
+ Cleans all of self.data and populates self._errors and
+ self._non_form_errors.
+ """
+ self._errors = []
+ self._non_form_errors = self.error_class()
+
+ if not self.is_bound: # Stop further processing.
+ return
+ for i in range(0, self.total_form_count()):
+ form = self.forms[i]
+ self._errors.append(form.errors)
+ try:
+ if (self.validate_max and
+ self.total_form_count() - len(self.deleted_forms) > self.max_num) or \
+ self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max:
+ raise ValidationError(ungettext(
+ "Please submit %d or fewer forms.",
+ "Please submit %d or fewer forms.", self.max_num) % self.max_num,
+ code='too_many_forms',
+ )
+ # Give self.clean() a chance to do cross-form validation.
+ self.clean()
+ except ValidationError as e:
+ self._non_form_errors = self.error_class(e.messages)
+
+ def clean(self):
+ """
+ Hook for doing any extra formset-wide cleaning after Form.clean() has
+ been called on every form. Any ValidationError raised by this method
+ will not be associated with a particular form; it will be accesible
+ via formset.non_form_errors()
+ """
+ pass
+
+ def has_changed(self):
+ """
+ Returns true if data in any form differs from initial.
+ """
+ return any(form.has_changed() for form in self)
+
+ def add_fields(self, form, index):
+ """A hook for adding extra fields on to each form instance."""
+ if self.can_order:
+ # Only pre-fill the ordering field for initial forms.
+ if index is not None and index < self.initial_form_count():
+ form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), initial=index+1, required=False)
+ else:
+ form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), required=False)
+ if self.can_delete:
+ form.fields[DELETION_FIELD_NAME] = BooleanField(label=_('Delete'), required=False)
+
+ def add_prefix(self, index):
+ return '%s-%s' % (self.prefix, index)
+
+ def is_multipart(self):
+ """
+ Returns True if the formset needs to be multipart, i.e. it
+ has FileInput. Otherwise, False.
+ """
+ if self.forms:
+ return self.forms[0].is_multipart()
+ else:
+ return self.empty_form.is_multipart()
+
+ @property
+ def media(self):
+ # All the forms on a FormSet are the same, so you only need to
+ # interrogate the first form for media.
+ if self.forms:
+ return self.forms[0].media
+ else:
+ return self.empty_form.media
+
+ def as_table(self):
+ "Returns this formset rendered as HTML <tr>s -- excluding the <table></table>."
+ # XXX: there is no semantic division between forms here, there
+ # probably should be. It might make sense to render each form as a
+ # table row with each field as a td.
+ forms = ' '.join([form.as_table() for form in self])
+ return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
+
+ def as_p(self):
+ "Returns this formset rendered as HTML <p>s."
+ forms = ' '.join([form.as_p() for form in self])
+ return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
+
+ def as_ul(self):
+ "Returns this formset rendered as HTML <li>s."
+ forms = ' '.join([form.as_ul() for form in self])
+ return mark_safe('\n'.join([six.text_type(self.management_form), forms]))
+
+def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
+ can_delete=False, max_num=None, validate_max=False):
+ """Return a FormSet for the given form class."""
+ if max_num is None:
+ max_num = DEFAULT_MAX_NUM
+ # hard limit on forms instantiated, to prevent memory-exhaustion attacks
+ # limit is simply max_num + DEFAULT_MAX_NUM (which is 2*DEFAULT_MAX_NUM
+ # if max_num is None in the first place)
+ absolute_max = max_num + DEFAULT_MAX_NUM
+ attrs = {'form': form, 'extra': extra,
+ 'can_order': can_order, 'can_delete': can_delete,
+ 'max_num': max_num, 'absolute_max': absolute_max,
+ 'validate_max' : validate_max}
+ return type(form.__name__ + str('FormSet'), (formset,), attrs)
+
+def all_valid(formsets):
+ """Returns true if every formset in formsets is valid."""
+ valid = True
+ for formset in formsets:
+ if not formset.is_valid():
+ valid = False
+ return valid