1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

newforms-admin: Cleaned up FormSet internals and renamed formset.form_list to formset.forms. Better, but still not quite there.

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5325 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2007-05-24 03:38:26 +00:00
parent c2cdd2d4b4
commit 3eab964332
3 changed files with 85 additions and 75 deletions

View File

@ -226,6 +226,12 @@ class BaseForm(StrAndUnicode):
""" """
return self.cleaned_data return self.cleaned_data
def reset(self):
"""Return this form to the state it was in before data was passed to it."""
self.data = {}
self.is_bound = False
self.__errors = None
class Form(BaseForm): class Form(BaseForm):
"A collection of Fields, plus their associated data." "A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way # This is a separate class from BaseForm in order to abstract the way

View File

@ -5,11 +5,6 @@ FORM_COUNT_FIELD_NAME = 'COUNT'
ORDERING_FIELD_NAME = 'ORDER' ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE' DELETION_FIELD_NAME = 'DELETE'
def formset_for_form(form, num_extra=1, orderable=False, deletable=False):
"""Return a FormSet for the given form class."""
attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable}
return type(form.__name__ + 'FormSet', (BaseFormSet,), attrs)
class ManagementForm(forms.Form): class ManagementForm(forms.Form):
""" """
``ManagementForm`` is used to keep track of how many form instances ``ManagementForm`` is used to keep track of how many form instances
@ -35,42 +30,62 @@ class BaseFormSet(object):
if self.management_form.is_valid(): if self.management_form.is_valid():
self.total_forms = self.management_form.cleaned_data[FORM_COUNT_FIELD_NAME] self.total_forms = self.management_form.cleaned_data[FORM_COUNT_FIELD_NAME]
self.required_forms = self.total_forms - self.num_extra self.required_forms = self.total_forms - self.num_extra
self.change_form_count = self.total_forms - self.num_extra
else: else:
# not sure that ValidationError is the best thing to raise here # not sure that ValidationError is the best thing to raise here
raise forms.ValidationError('ManagementForm data is missing or has been tampered with') raise forms.ValidationError('ManagementForm data is missing or has been tampered with')
elif initial: elif initial:
self.change_form_count = len(initial)
self.required_forms = len(initial) self.required_forms = len(initial)
self.total_forms = self.required_forms + self.num_extra self.total_forms = self.required_forms + self.num_extra
self.management_form = ManagementForm(initial={FORM_COUNT_FIELD_NAME: self.total_forms}, auto_id=self.auto_id, prefix=self.prefix) self.management_form = ManagementForm(initial={FORM_COUNT_FIELD_NAME: self.total_forms}, auto_id=self.auto_id, prefix=self.prefix)
else: else:
self.change_form_count = 0
self.required_forms = 0 self.required_forms = 0
self.total_forms = self.num_extra self.total_forms = self.num_extra
self.management_form = ManagementForm(initial={FORM_COUNT_FIELD_NAME: self.total_forms}, auto_id=self.auto_id, prefix=self.prefix) self.management_form = ManagementForm(initial={FORM_COUNT_FIELD_NAME: self.total_forms}, auto_id=self.auto_id, prefix=self.prefix)
def _get_form_list(self): def _get_add_forms(self):
"""Return a list of Form instances.""" """Return a list of all the change forms in this ``FormSet``."""
if not hasattr(self, '_form_list'): Form = self.form_class
self._form_list = [] if not hasattr(self, '_add_forms'):
for i in range(0, self.total_forms): add_forms = []
kwargs = {'data': self.data, 'auto_id': self.auto_id, 'prefix': self.add_prefix(i)} for i in range(self.change_form_count, self.total_forms):
if self.initial and i < self.required_forms: kwargs = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
kwargs['initial'] = self.initial[i] if self.data:
form_instance = self.form_class(**kwargs) kwargs['data'] = self.data
# HACK: if the form was not completed, replace it with a blank one add_form = Form(**kwargs)
if self.data and i >= self.required_forms and form_instance.is_empty(): self.add_fields(add_form, i)
form_instance = self.form_class(auto_id=self.auto_id, prefix=self.add_prefix(i)) add_forms.append(add_form)
self.add_fields(form_instance, i) self._add_forms = add_forms
self._form_list.append(form_instance) return self._add_forms
return self._form_list add_forms = property(_get_add_forms)
form_list = property(_get_form_list) def _get_change_forms(self):
"""Return a list of all the change forms in this ``FormSet``."""
Form = self.form_class
if not hasattr(self, '_add_forms'):
change_forms = []
for i in range(0, self.change_form_count):
kwargs = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
if self.data:
kwargs['data'] = self.data
if self.initial:
kwargs['initial'] = self.initial[i]
change_form = Form(**kwargs)
self.add_fields(change_form, i)
change_forms.append(change_form)
self._change_forms= change_forms
return self._change_forms
change_forms = property(_get_change_forms)
def _forms(self):
return self.change_forms + self.add_forms
forms = property(_forms)
def full_clean(self): def full_clean(self):
""" """Cleans all of self.data and populates self.__errors and self.cleaned_data."""
Cleans all of self.data and populates self.__errors and self.cleaned_data.
"""
is_valid = True is_valid = True
errors = [] errors = []
if not self.is_bound: # Stop further processing. if not self.is_bound: # Stop further processing.
self.__errors = errors self.__errors = errors
@ -78,50 +93,39 @@ class BaseFormSet(object):
cleaned_data = [] cleaned_data = []
deleted_data = [] deleted_data = []
self._form_list = [] # Process change forms
# step backwards through the forms so when we hit the first filled one for form in self.change_forms:
# we can easily require the rest without backtracking if form.is_valid():
required = False
for i in range(self.total_forms-1, -1, -1):
kwargs = {'data': self.data, 'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
# prep initial data if there is some
if self.initial and i < self.required_forms:
kwargs['initial'] = self.initial[i]
# create the form instance
form = self.form_class(**kwargs)
self.add_fields(form, i)
if self.data and (i < self.required_forms or not form.is_empty(exceptions=[ORDERING_FIELD_NAME])):
required = True # forms cannot be empty anymore
# HACK: if the form is empty and not required, replace it with a blank one
# this is necessary to keep form.errors empty
if not required and self.data and form.is_empty(exceptions=[ORDERING_FIELD_NAME]):
form = self.form_class(auto_id=self.auto_id, prefix=self.add_prefix(i))
self.add_fields(form, i)
else:
# if the formset is still vaild overall and this form instance
# is valid, keep appending to cleaned_data
if is_valid and form.is_valid():
if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]: if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
deleted_data.append(form.cleaned_data) deleted_data.append(form.cleaned_data)
else: else:
cleaned_data.append(form.cleaned_data) cleaned_data.append(form.cleaned_data)
else: else:
is_valid = False is_valid = False
# append to errors regardless
errors.append(form.errors) errors.append(form.errors)
self._form_list.append(form)
deleted_data.reverse() # Process add forms in reverse so we can easily tell when the remaining
# ones should be required.
required = False
add_errors = []
for i in range(len(self.add_forms)-1, -1, -1):
form = self.add_forms[i]
# If an add form is empty, reset it so it won't have any errors
if form.is_empty([ORDERING_FIELD_NAME]) and not required:
form.reset()
continue
else:
required = True
if form.is_valid():
cleaned_data.append(form.cleaned_data)
else:
is_valid = False
add_errors.append(form.errors)
add_errors.reverse()
errors.extend(add_errors)
if self.orderable: if self.orderable:
cleaned_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME]) cleaned_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME])
else:
cleaned_data.reverse()
errors.reverse()
self._form_list.reverse()
if is_valid: if is_valid:
self.cleaned_data = cleaned_data self.cleaned_data = cleaned_data
@ -129,8 +133,6 @@ class BaseFormSet(object):
self.errors = errors self.errors = errors
self._is_valid = is_valid self._is_valid = is_valid
# TODO: user defined formset validation.
def add_fields(self, form, index): def add_fields(self, form, index):
"""A hook for adding extra fields on to each form instance.""" """A hook for adding extra fields on to each form instance."""
if self.orderable: if self.orderable:
@ -145,5 +147,7 @@ class BaseFormSet(object):
self.full_clean() self.full_clean()
return self._is_valid return self._is_valid
# TODO: handle deletion and ordering in the same FormSet def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False):
# TODO: model integration: form_for_instance and form_for_model type functions """Return a FormSet for the given form class."""
attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable}
return type(form.__name__ + 'FormSet', (formset,), attrs)

View File

@ -20,7 +20,7 @@ for adding data. By default, it displays 1 blank form. It can display more,
but we'll look at how to do so later. but we'll look at how to do so later.
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" /></li> <li>Choice: <input type="text" name="choices-0-choice" /></li>
<li>Votes: <input type="text" name="choices-0-votes" /></li> <li>Votes: <input type="text" name="choices-0-votes" /></li>
@ -78,7 +78,7 @@ an extra blank form is included.
>>> initial = [{'choice': u'Calexico', 'votes': 100}] >>> initial = [{'choice': u'Calexico', 'votes': 100}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
@ -150,7 +150,7 @@ num_extra argument to formset_for_form.
>>> ChoiceFormSet = formset_for_form(Choice, num_extra=3) >>> ChoiceFormSet = formset_for_form(Choice, num_extra=3)
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" /></li> <li>Choice: <input type="text" name="choices-0-choice" /></li>
<li>Votes: <input type="text" name="choices-0-votes" /></li> <li>Votes: <input type="text" name="choices-0-votes" /></li>
@ -223,7 +223,7 @@ data.
>>> initial = [{'choice': u'Calexico', 'votes': 100}] >>> initial = [{'choice': u'Calexico', 'votes': 100}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
@ -268,7 +268,7 @@ rather than formset.cleaned_data
>>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}] >>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
@ -318,7 +318,7 @@ something at the front of the list, you'd need to set it's order to 0.
>>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}] >>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
@ -364,7 +364,7 @@ Let's try throwing ordering and deletion into the same form.
... {'choice': u'The Decemberists', 'votes': 500}, ... {'choice': u'The Decemberists', 'votes': 500},
... ] ... ]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices') >>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.form_list: >>> for form in formset.forms:
... print form.as_ul() ... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li> <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li> <li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>