summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Rosner <brosner@gmail.com>2008-06-11 03:58:01 +0000
committerBrian Rosner <brosner@gmail.com>2008-06-11 03:58:01 +0000
commit530670e27f338923c0199dc274f2ecf71af5d762 (patch)
tree4ab45d4ccc57c50d7c5bcc1521bc151618444686
parentedf396da59d44efebe6d5d7242bda30de4b1afeb (diff)
newforms-admin: Fixed #6075 -- Implemented max_num on formsets and model formsets. Added a hook on InlineModelAdmin to customize in the admin interface.
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7613 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/contrib/admin/options.py3
-rw-r--r--django/newforms/formsets.py21
-rw-r--r--django/newforms/models.py30
-rw-r--r--docs/modelforms.txt28
-rw-r--r--docs/newforms.txt16
-rw-r--r--tests/modeltests/model_formsets/models.py23
-rw-r--r--tests/regressiontests/forms/formsets.py75
7 files changed, 170 insertions, 26 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 1aa56f2c91..8ba543d4a7 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -699,6 +699,7 @@ class InlineModelAdmin(BaseModelAdmin):
fk_name = None
formset = BaseInlineFormset
extra = 3
+ max_num = 0
template = None
verbose_name = None
verbose_name_plural = None
@@ -722,7 +723,7 @@ class InlineModelAdmin(BaseModelAdmin):
return inlineformset_factory(self.parent_model, self.model,
form=self.form, formset=self.formset, fk_name=self.fk_name,
fields=fields, formfield_callback=self.formfield_for_dbfield,
- extra=self.extra)
+ extra=self.extra, max_num=self.max_num)
def get_fieldsets(self, request, obj=None):
if self.declared_fieldsets:
diff --git a/django/newforms/formsets.py b/django/newforms/formsets.py
index 2edc673a32..03f5278c30 100644
--- a/django/newforms/formsets.py
+++ b/django/newforms/formsets.py
@@ -10,6 +10,7 @@ __all__ = ('BaseFormSet', 'all_valid')
# special field names
TOTAL_FORM_COUNT = 'TOTAL_FORMS'
INITIAL_FORM_COUNT = 'INITIAL_FORMS'
+MAX_FORM_COUNT = 'MAX_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'
@@ -22,6 +23,7 @@ class ManagementForm(Form):
def __init__(self, *args, **kwargs):
self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
+ self.base_fields[MAX_FORM_COUNT] = IntegerField(widget=HiddenInput)
super(ManagementForm, self).__init__(*args, **kwargs)
class BaseFormSet(StrAndUnicode):
@@ -29,7 +31,7 @@ class BaseFormSet(StrAndUnicode):
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):
+ initial=None, error_class=ErrorList):
self.is_bound = data is not None or files is not None
self.prefix = prefix or 'form'
self.auto_id = auto_id
@@ -45,18 +47,25 @@ class BaseFormSet(StrAndUnicode):
if self.management_form.is_valid():
self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT]
self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
+ self._max_form_count = self.management_form.cleaned_data[MAX_FORM_COUNT]
else:
raise ValidationError('ManagementForm data is missing or has been tampered with')
else:
if initial:
self._initial_form_count = len(initial)
+ if self._initial_form_count > self._max_form_count and self._max_form_count > 0:
+ self._initial_form_count = self._max_form_count
self._total_form_count = self._initial_form_count + self.extra
else:
self._initial_form_count = 0
self._total_form_count = self.extra
- initial = {TOTAL_FORM_COUNT: self._total_form_count, INITIAL_FORM_COUNT: self._initial_form_count}
+ if self._total_form_count > self._max_form_count and self._max_form_count > 0:
+ self._total_form_count = self._max_form_count
+ initial = {TOTAL_FORM_COUNT: self._total_form_count,
+ INITIAL_FORM_COUNT: self._initial_form_count,
+ MAX_FORM_COUNT: self._max_form_count}
self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)
-
+
# construct the forms in the formset
self._construct_forms()
@@ -266,9 +275,11 @@ class BaseFormSet(StrAndUnicode):
return mark_safe(u'\n'.join([unicode(self.management_form), forms]))
def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
- can_delete=False):
+ can_delete=False, max_num=0):
"""Return a FormSet for the given form class."""
- attrs = {'form': form, 'extra': extra, 'can_order': can_order, 'can_delete': can_delete}
+ attrs = {'form': form, 'extra': extra,
+ 'can_order': can_order, 'can_delete': can_delete,
+ '_max_form_count': max_num}
return type(form.__name__ + 'FormSet', (formset,), attrs)
def all_valid(formsets):
diff --git a/django/newforms/models.py b/django/newforms/models.py
index b9baced609..a60c1793a7 100644
--- a/django/newforms/models.py
+++ b/django/newforms/models.py
@@ -305,7 +305,11 @@ class BaseModelFormSet(BaseFormSet):
queryset=None, **kwargs):
self.queryset = queryset
defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
- defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()]
+ if self._max_form_count > 0:
+ qs = self.get_queryset()[:self._max_form_count]
+ else:
+ qs = self.get_queryset()
+ defaults['initial'] = [model_to_dict(obj) for obj in qs]
defaults.update(kwargs)
super(BaseModelFormSet, self).__init__(**defaults)
@@ -369,15 +373,16 @@ class BaseModelFormSet(BaseFormSet):
super(BaseModelFormSet, self).add_fields(form, index)
def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
- formset=BaseModelFormSet,
- extra=1, can_delete=False, can_order=False,
- fields=None, exclude=None):
+ formset=BaseModelFormSet,
+ extra=1, can_delete=False, can_order=False,
+ max_num=0, fields=None, exclude=None):
"""
Returns a FormSet class for the given Django model class.
"""
form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
- formfield_callback=formfield_callback)
- FormSet = formset_factory(form, formset, extra=extra, can_order=can_order, can_delete=can_delete)
+ formfield_callback=formfield_callback)
+ FormSet = formset_factory(form, formset, extra=extra, max_num=max_num,
+ can_order=can_order, can_delete=can_delete)
FormSet.model = model
return FormSet
@@ -395,9 +400,8 @@ class BaseInlineFormset(BaseModelFormSet):
super(BaseInlineFormset, self).__init__(data, files, prefix=self.rel_name)
def _construct_forms(self):
- from django.newforms.formsets import INITIAL_FORM_COUNT
if self.save_as_new:
- self._total_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
+ self._total_form_count = self._initial_form_count
self._initial_form_count = 0
super(BaseInlineFormset, self)._construct_forms()
@@ -443,10 +447,10 @@ def _get_foreign_key(parent_model, model, fk_name=None):
def inlineformset_factory(parent_model, model, form=ModelForm,
- formset=BaseInlineFormset, fk_name=None,
- fields=None, exclude=None,
- extra=3, can_order=False, can_delete=True,
- formfield_callback=lambda f: f.formfield()):
+ formset=BaseInlineFormset, fk_name=None,
+ fields=None, exclude=None,
+ extra=3, can_order=False, can_delete=True, max_num=0,
+ formfield_callback=lambda f: f.formfield()):
"""
Returns an ``InlineFormset`` for the given kwargs.
@@ -464,7 +468,7 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
formfield_callback=formfield_callback,
formset=formset,
extra=extra, can_delete=can_delete, can_order=can_order,
- fields=fields, exclude=exclude)
+ fields=fields, exclude=exclude, max_num=max_num)
FormSet.fk = fk
return FormSet
diff --git a/docs/modelforms.txt b/docs/modelforms.txt
index b9332bc59e..f2222c1fc0 100644
--- a/docs/modelforms.txt
+++ b/docs/modelforms.txt
@@ -449,6 +449,34 @@ model instances without any database interaction::
This gives you the ability to attach data to the instances before saving them
to the database.
+Limiting the number of objects editable
+---------------------------------------
+
+Similar to regular formsets you can use the ``max_num`` parameter to
+``modelformset_factory`` to limit the number of forms displayed. With
+model formsets this will properly limit the query to only select the maximum
+number of objects needed::
+
+ >>> Author.objects.order_by('name')
+ [<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>]
+
+ >>> AuthorFormSet = modelformset_factory(Author, max_num=2, extra=1)
+ >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name'))
+ >>> formset.initial
+ [{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}]
+
+If the value of ``max_num`` is less than the total objects returned it will
+fill the rest with extra forms::
+
+ >>> AuthorFormSet = modelformset_factory(Author, max_num=4, extra=1)
+ >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name'))
+ >>> for form in formset.forms:
+ ... print form.as_table()
+ <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100" /><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></td></tr>
+ <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100" /><input type="hidden" name="form-1-id" value="3" id="id_form-1-id" /></td></tr>
+ <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100" /><input type="hidden" name="form-2-id" value="2" id="id_form-2-id" /></td></tr>
+ <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></td></tr>
+
Using ``inlineformset_factory``
-------------------------------
diff --git a/docs/newforms.txt b/docs/newforms.txt
index ad3f81cb6c..f81fcfc8dc 100644
--- a/docs/newforms.txt
+++ b/docs/newforms.txt
@@ -2230,6 +2230,22 @@ There are now a total of three forms showing above. One for the initial data
that was passed in and two extra forms. Also note that we are passing in a
list of dictionaries as the initial data.
+Limiting the maximum number of forms
+------------------------------------
+
+The ``max_num`` parameter to ``formset_factory`` gives you the ability to
+force the maximum number of forms the formset will display::
+
+ >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1)
+ >>> formset = ArticleFormset()
+ >>> for form in formset.forms:
+ ... print form.as_table()
+ <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
+ <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
+
+The default value of ``max_num`` is ``0`` which is the same as saying put no
+limit on the number forms displayed.
+
Formset validation
------------------
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
index 93bc80ee72..93519a4421 100644
--- a/tests/modeltests/model_formsets/models.py
+++ b/tests/modeltests/model_formsets/models.py
@@ -31,6 +31,7 @@ __test__ = {'API_TESTS': """
>>> data = {
... 'form-TOTAL_FORMS': '3', # the number of forms rendered
... 'form-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'form-MAX_FORMS': '0', # the max number of forms
... 'form-0-name': 'Charles Baudelaire',
... 'form-1-name': 'Arthur Rimbaud',
... 'form-2-name': '',
@@ -68,6 +69,7 @@ them in alphabetical order by name.
>>> data = {
... 'form-TOTAL_FORMS': '3', # the number of forms rendered
... 'form-INITIAL_FORMS': '2', # the number of forms with initial data
+... 'form-MAX_FORMS': '0', # the max number of forms
... 'form-0-id': '2',
... 'form-0-name': 'Arthur Rimbaud',
... 'form-1-id': '1',
@@ -111,6 +113,7 @@ deltetion, make sure we don't save that form.
>>> data = {
... 'form-TOTAL_FORMS': '4', # the number of forms rendered
... 'form-INITIAL_FORMS': '3', # the number of forms with initial data
+... 'form-MAX_FORMS': '0', # the max number of forms
... 'form-0-id': '2',
... 'form-0-name': 'Arthur Rimbaud',
... 'form-1-id': '1',
@@ -140,6 +143,7 @@ Let's edit a record to ensure save only returns that one record.
>>> data = {
... 'form-TOTAL_FORMS': '4', # the number of forms rendered
... 'form-INITIAL_FORMS': '3', # the number of forms with initial data
+... 'form-MAX_FORMS': '0', # the max number of forms
... 'form-0-id': '2',
... 'form-0-name': 'Walt Whitman',
... 'form-1-id': '1',
@@ -158,6 +162,22 @@ True
>>> formset.save()
[<Author: Walt Whitman>]
+Test the behavior of max_num with model formsets. It should properly limit
+the queryset to reduce the amount of objects being pulled in when not being
+used.
+
+>>> qs = Author.objects.order_by('name')
+
+>>> AuthorFormSet = modelformset_factory(Author, max_num=2)
+>>> formset = AuthorFormSet(queryset=qs)
+>>> formset.initial
+[{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}]
+
+>>> AuthorFormSet = modelformset_factory(Author, max_num=3)
+>>> formset = AuthorFormSet(queryset=qs)
+>>> formset.initial
+[{'id': 1, 'name': u'Charles Baudelaire'}, {'id': 3, 'name': u'Paul Verlaine'}, {'id': 2, 'name': u'Walt Whitman'}]
+
# Inline Formsets ############################################################
We can also create a formset that is tied to a parent model. This is how the
@@ -178,6 +198,7 @@ admin system's edit inline functionality works.
>>> data = {
... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered
... 'book_set-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'book_set-MAX_FORMS': '0', # the max number of forms
... 'book_set-0-title': 'Les Fleurs du Mal',
... 'book_set-1-title': '',
... 'book_set-2-title': '',
@@ -212,6 +233,7 @@ book.
>>> data = {
... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered
... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data
+... 'book_set-MAX_FORMS': '0', # the max number of forms
... 'book_set-0-id': '1',
... 'book_set-0-title': 'Les Fleurs du Mal',
... 'book_set-1-title': 'Le Spleen de Paris',
@@ -238,6 +260,7 @@ This is used in the admin for save_as functionality.
>>> data = {
... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered
... 'book_set-INITIAL_FORMS': '2', # the number of forms with initial data
+... 'book_set-MAX_FORMS': '0', # the max number of forms
... 'book_set-0-id': '1',
... 'book_set-0-title': 'Les Fleurs du Mal',
... 'book_set-1-id': '2',
diff --git a/tests/regressiontests/forms/formsets.py b/tests/regressiontests/forms/formsets.py
index 4cccb10d09..dedc0a8e52 100644
--- a/tests/regressiontests/forms/formsets.py
+++ b/tests/regressiontests/forms/formsets.py
@@ -20,7 +20,7 @@ but we'll look at how to do so later.
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
>>> print formset
-<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" />
+<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_FORMS" value="0" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>
@@ -34,6 +34,7 @@ the TOTAL_FORMS field appropriately.
>>> data = {
... 'choices-TOTAL_FORMS': '1', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... }
@@ -60,6 +61,7 @@ any of the forms.
>>> data = {
... 'choices-TOTAL_FORMS': '1', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '',
... }
@@ -90,6 +92,7 @@ Let's simulate what would happen if we submitted this form.
>>> data = {
... 'choices-TOTAL_FORMS': '2', # the number of forms rendered
... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': '',
@@ -111,6 +114,7 @@ handle that later.
>>> data = {
... 'choices-TOTAL_FORMS': '2', # the number of forms rendered
... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': 'The Decemberists',
@@ -130,6 +134,7 @@ handle that case later.
>>> data = {
... 'choices-TOTAL_FORMS': '2', # the number of forms rendered
... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': '', # deleted value
... 'choices-0-votes': '', # deleted value
... 'choices-1-choice': '',
@@ -167,6 +172,7 @@ number of forms to be completed.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': '',
... 'choices-0-votes': '',
... 'choices-1-choice': '',
@@ -187,6 +193,7 @@ We can just fill out one of the forms.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': '',
@@ -207,6 +214,7 @@ And once again, if we try to partially complete a form, validation will fail.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': 'The Decemberists',
@@ -267,6 +275,7 @@ To delete something, we just need to set that form's special delete field to
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '2', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-DELETE': '',
@@ -316,6 +325,7 @@ something at the front of the list, you'd need to set it's order to 0.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '2', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-ORDER': '1',
@@ -342,6 +352,7 @@ they will be sorted below everything else.
>>> data = {
... 'choices-TOTAL_FORMS': '4', # the number of forms rendered
... 'choices-INITIAL_FORMS': '3', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-ORDER': '1',
@@ -403,6 +414,7 @@ Let's delete Fergie, and put The Decemberists ahead of Calexico.
>>> data = {
... 'choices-TOTAL_FORMS': '4', # the number of forms rendered
... 'choices-INITIAL_FORMS': '3', # the number of forms with initial data
+... 'choices-MAX_FORMS': '0', # the max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-ORDER': '1',
@@ -444,12 +456,7 @@ error if there are any duplicates.
... name = CharField()
...
->>> class FavoriteDrinksFormSet(BaseFormSet):
-... form = FavoriteDrinkForm
-... extra = 2
-... can_order = False
-... can_delete = False
-...
+>>> class BaseFavoriteDrinksFormSet(BaseFormSet):
... def clean(self):
... seen_drinks = []
... for drink in self.cleaned_data:
@@ -458,11 +465,15 @@ error if there are any duplicates.
... seen_drinks.append(drink['name'])
...
+>>> FavoriteDrinksFormSet = formset_factory(FavoriteDrinkForm,
+... formset=BaseFavoriteDrinksFormSet, extra=3)
+
We start out with a some duplicate data.
>>> data = {
... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered
... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'drinks-MAX_FORMS': '0', # the max number of forms
... 'drinks-0-name': 'Gin and Tonic',
... 'drinks-1-name': 'Gin and Tonic',
... }
@@ -484,6 +495,7 @@ Make sure we didn't break the valid case.
>>> data = {
... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered
... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
+... 'drinks-MAX_FORMS': '0', # the max number of forms
... 'drinks-0-name': 'Gin and Tonic',
... 'drinks-1-name': 'Bloody Mary',
... }
@@ -494,6 +506,55 @@ True
>>> for error in formset.non_form_errors():
... print error
+# Limiting the maximum number of forms ########################################
+
+# Base case for max_num.
+
+>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=5, max_num=2)
+>>> formset = LimitedFavoriteDrinkFormSet()
+>>> for form in formset.forms:
+... print form
+<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
+<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
+
+# Ensure the that max_num has no affect when extra is less than max_forms.
+
+>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
+>>> formset = LimitedFavoriteDrinkFormSet()
+>>> for form in formset.forms:
+... print form
+<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
+
+# max_num with initial data
+
+# More initial forms than max_num will result in only the first max_num of
+# them to be displayed with no extra forms.
+
+>>> initial = [
+... {'name': 'Gin Tonic'},
+... {'name': 'Bloody Mary'},
+... {'name': 'Jack and Coke'},
+... ]
+>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
+>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
+>>> for form in formset.forms:
+... print form
+<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
+<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" value="Bloody Mary" id="id_form-1-name" /></td></tr>
+
+# One form from initial and extra=3 with max_num=2 should result in the one
+# initial form and one extra.
+
+>>> initial = [
+... {'name': 'Gin Tonic'},
+... ]
+>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=2)
+>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
+>>> for form in formset.forms:
+... print form
+<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
+<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
+
# Regression test for #6926 ##################################################