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.
This commit is contained in:
parent
9be93aa809
commit
ef79582e86
|
@ -6,6 +6,7 @@ from django.forms.fields import IntegerField, BooleanField
|
||||||
from django.forms.util import ErrorList
|
from django.forms.util import ErrorList
|
||||||
from django.forms.widgets import HiddenInput
|
from django.forms.widgets import HiddenInput
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
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.safestring import mark_safe
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
|
@ -55,8 +56,6 @@ class BaseFormSet(object):
|
||||||
self.error_class = error_class
|
self.error_class = error_class
|
||||||
self._errors = None
|
self._errors = None
|
||||||
self._non_form_errors = None
|
self._non_form_errors = None
|
||||||
# construct the forms in the formset
|
|
||||||
self._construct_forms()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.as_table()
|
return self.as_table()
|
||||||
|
@ -125,12 +124,14 @@ class BaseFormSet(object):
|
||||||
initial_forms = len(self.initial) if self.initial else 0
|
initial_forms = len(self.initial) if self.initial else 0
|
||||||
return initial_forms
|
return initial_forms
|
||||||
|
|
||||||
def _construct_forms(self):
|
@cached_property
|
||||||
# instantiate all the forms and put them in self.forms
|
def forms(self):
|
||||||
self.forms = []
|
"""
|
||||||
|
Instantiate forms at first property access.
|
||||||
|
"""
|
||||||
# DoS protection is included in total_form_count()
|
# DoS protection is included in total_form_count()
|
||||||
for i in xrange(self.total_form_count()):
|
forms = [self._construct_form(i) for i in xrange(self.total_form_count())]
|
||||||
self.forms.append(self._construct_form(i))
|
return forms
|
||||||
|
|
||||||
def _construct_form(self, i, **kwargs):
|
def _construct_form(self, i, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1072,7 +1072,8 @@ ArticleFormSet = formset_factory(ArticleForm)
|
||||||
|
|
||||||
class TestIsBoundBehavior(TestCase):
|
class TestIsBoundBehavior(TestCase):
|
||||||
def test_no_data_raises_validation_error(self):
|
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):
|
def test_with_management_data_attrs_work_fine(self):
|
||||||
data = {
|
data = {
|
||||||
|
|
|
@ -8,7 +8,7 @@ from decimal import Decimal
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms.models import (_get_foreign_key, inlineformset_factory,
|
from django.forms.models import (_get_foreign_key, inlineformset_factory,
|
||||||
modelformset_factory)
|
modelformset_factory, BaseModelFormSet)
|
||||||
from django.test import TestCase, skipUnlessDBFeature
|
from django.test import TestCase, skipUnlessDBFeature
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
@ -386,6 +386,23 @@ class ModelFormsetTest(TestCase):
|
||||||
formset = PostFormSet()
|
formset = PostFormSet()
|
||||||
self.assertFalse("subtitle" in formset.forms[0].fields)
|
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):
|
def test_model_inheritance(self):
|
||||||
BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
|
BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
|
||||||
formset = BetterAuthorFormSet()
|
formset = BetterAuthorFormSet()
|
||||||
|
|
Loading…
Reference in New Issue