diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index fd98c43..e3a729b 100644
a
|
b
|
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 | 8 | from django.utils.encoding import python_2_unicode_compatible |
| 9 | from django.utils.functional import cached_property |
9 | 10 | from django.utils.safestring import mark_safe |
10 | 11 | from django.utils import six |
11 | 12 | from django.utils.six.moves import xrange |
… |
… |
class BaseFormSet(object):
|
55 | 56 | self.error_class = error_class |
56 | 57 | self._errors = None |
57 | 58 | self._non_form_errors = None |
58 | | # construct the forms in the formset |
59 | | self._construct_forms() |
60 | 59 | |
61 | 60 | def __str__(self): |
62 | 61 | return self.as_table() |
… |
… |
class BaseFormSet(object):
|
122 | 121 | initial_forms = len(self.initial) if self.initial else 0 |
123 | 122 | return initial_forms |
124 | 123 | |
125 | | def _construct_forms(self): |
126 | | # instantiate all the forms and put them in self.forms |
127 | | self.forms = [] |
| 124 | @cached_property |
| 125 | def forms(self): |
| 126 | """ |
| 127 | Instantiate forms at first property access. |
| 128 | """ |
128 | 129 | # DoS protection is included in total_form_count() |
129 | | for i in xrange(self.total_form_count()): |
130 | | self.forms.append(self._construct_form(i)) |
| 130 | forms = [self._construct_form(i) for i in xrange(self.total_form_count())] |
| 131 | return forms |
131 | 132 | |
132 | 133 | def _construct_form(self, i, **kwargs): |
133 | 134 | """ |
diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py
index 1e9e7db3..eca3f7d 100644
a
|
b
|
ArticleFormSet = formset_factory(ArticleForm)
|
1070 | 1070 | |
1071 | 1071 | class TestIsBoundBehavior(TestCase): |
1072 | 1072 | def test_no_data_raises_validation_error(self): |
1073 | | self.assertRaises(ValidationError, ArticleFormSet, {}) |
| 1073 | with self.assertRaises(ValidationError): |
| 1074 | ArticleFormSet({}).is_valid() |
1074 | 1075 | |
1075 | 1076 | def test_with_management_data_attrs_work_fine(self): |
1076 | 1077 | data = { |
diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py
index 03cd3b0..4a8b363 100644
a
|
b
|
from decimal import Decimal
|
8 | 8 | from django import forms |
9 | 9 | from django.db import models |
10 | 10 | from django.forms.models import (_get_foreign_key, inlineformset_factory, |
11 | | modelformset_factory) |
| 11 | modelformset_factory, BaseModelFormSet) |
12 | 12 | from django.test import TestCase, skipUnlessDBFeature |
13 | 13 | from django.utils import six |
14 | 14 | |
… |
… |
class ModelFormsetTest(TestCase):
|
386 | 386 | formset = PostFormSet() |
387 | 387 | self.assertFalse("subtitle" in formset.forms[0].fields) |
388 | 388 | |
| 389 | def test_custom_queryset_init(self): |
| 390 | """ |
| 391 | Test that a queryset can be overriden in the __init__ method. |
| 392 | https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset |
| 393 | """ |
| 394 | author1 = Author.objects.create(name='Charles Baudelaire') |
| 395 | author2 = Author.objects.create(name='Paul Verlaine') |
| 396 | |
| 397 | class BaseAuthorFormSet(BaseModelFormSet): |
| 398 | def __init__(self, *args, **kwargs): |
| 399 | super(BaseAuthorFormSet, self).__init__(*args, **kwargs) |
| 400 | self.queryset = Author.objects.filter(name__startswith='Charles') |
| 401 | |
| 402 | AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet) |
| 403 | formset = AuthorFormSet() |
| 404 | self.assertEqual(len(formset.get_queryset()), 1) |
| 405 | |
389 | 406 | def test_model_inheritance(self): |
390 | 407 | BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__") |
391 | 408 | formset = BetterAuthorFormSet() |