Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed 17478 -- Allowed queryset overriding in BaseModelFormSet init

BaseModelFormSet.forms is now a cached property instead of being
populated in the __init__ method. This behaviour also matches an
example in the documentation.
Thanks Thomasz Swiderski for the report and Simon Charette for the
review.
  • Loading branch information...
commit ef79582e8630cb3c119caed52130c9671188addd 1 parent 9be93aa
Claude Paroz claudep authored
15 django/forms/formsets.py
View
@@ -6,6 +6,7 @@
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
@@ -55,8 +56,6 @@ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
self.error_class = error_class
self._errors = None
self._non_form_errors = None
- # construct the forms in the formset
- self._construct_forms()
def __str__(self):
return self.as_table()
@@ -125,12 +124,14 @@ def initial_form_count(self):
initial_forms = len(self.initial) if self.initial else 0
return initial_forms
- def _construct_forms(self):
- # instantiate all the forms and put them in self.forms
- self.forms = []
+ @cached_property
+ def forms(self):
+ """
+ Instantiate forms at first property access.
+ """
# DoS protection is included in total_form_count()
- for i in xrange(self.total_form_count()):
- self.forms.append(self._construct_form(i))
+ forms = [self._construct_form(i) for i in xrange(self.total_form_count())]
+ return forms
def _construct_form(self, i, **kwargs):
"""
3  tests/forms_tests/tests/test_formsets.py
View
@@ -1072,7 +1072,8 @@ class ArticleForm(Form):
class TestIsBoundBehavior(TestCase):
def test_no_data_raises_validation_error(self):
- self.assertRaises(ValidationError, ArticleFormSet, {})
+ with self.assertRaises(ValidationError):
+ ArticleFormSet({}).is_valid()
def test_with_management_data_attrs_work_fine(self):
data = {
19 tests/model_formsets/tests.py
View
@@ -8,7 +8,7 @@
from django import forms
from django.db import models
from django.forms.models import (_get_foreign_key, inlineformset_factory,
- modelformset_factory)
+ modelformset_factory, BaseModelFormSet)
from django.test import TestCase, skipUnlessDBFeature
from django.utils import six
@@ -386,6 +386,23 @@ class Meta:
formset = PostFormSet()
self.assertFalse("subtitle" in formset.forms[0].fields)
+ def test_custom_queryset_init(self):
+ """
+ Test that a queryset can be overriden in the __init__ method.
+ https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset
+ """
+ author1 = Author.objects.create(name='Charles Baudelaire')
+ author2 = Author.objects.create(name='Paul Verlaine')
+
+ class BaseAuthorFormSet(BaseModelFormSet):
+ def __init__(self, *args, **kwargs):
+ super(BaseAuthorFormSet, self).__init__(*args, **kwargs)
+ self.queryset = Author.objects.filter(name__startswith='Charles')
+
+ AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet)
+ formset = AuthorFormSet()
+ self.assertEqual(len(formset.get_queryset()), 1)
+
def test_model_inheritance(self):
BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
formset = BetterAuthorFormSet()
Please sign in to comment.
Something went wrong with that request. Please try again.