Merge pull request #4757 from sergei-maertens/ticket_18166
Fixed #18166 -- Added ability to pass kwargs to the form constructor in a formset.
This commit is contained in:
commit
fe21fb810a
|
@ -54,13 +54,14 @@ class BaseFormSet(object):
|
||||||
A collection of instances of the same Form class.
|
A collection of instances of the same Form class.
|
||||||
"""
|
"""
|
||||||
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
|
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
|
||||||
initial=None, error_class=ErrorList):
|
initial=None, error_class=ErrorList, form_kwargs=None):
|
||||||
self.is_bound = data is not None or files is not None
|
self.is_bound = data is not None or files is not None
|
||||||
self.prefix = prefix or self.get_default_prefix()
|
self.prefix = prefix or self.get_default_prefix()
|
||||||
self.auto_id = auto_id
|
self.auto_id = auto_id
|
||||||
self.data = data or {}
|
self.data = data or {}
|
||||||
self.files = files or {}
|
self.files = files or {}
|
||||||
self.initial = initial
|
self.initial = initial
|
||||||
|
self.form_kwargs = form_kwargs or {}
|
||||||
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
|
||||||
|
@ -139,9 +140,16 @@ class BaseFormSet(object):
|
||||||
Instantiate forms at first property access.
|
Instantiate forms at first property access.
|
||||||
"""
|
"""
|
||||||
# DoS protection is included in total_form_count()
|
# DoS protection is included in total_form_count()
|
||||||
forms = [self._construct_form(i) for i in range(self.total_form_count())]
|
forms = [self._construct_form(i, **self.get_form_kwargs(i))
|
||||||
|
for i in range(self.total_form_count())]
|
||||||
return forms
|
return forms
|
||||||
|
|
||||||
|
def get_form_kwargs(self, index):
|
||||||
|
"""
|
||||||
|
Return additional keyword arguments for each individual formset form.
|
||||||
|
"""
|
||||||
|
return self.form_kwargs.copy()
|
||||||
|
|
||||||
def _construct_form(self, i, **kwargs):
|
def _construct_form(self, i, **kwargs):
|
||||||
"""
|
"""
|
||||||
Instantiates and returns the i-th form instance in a formset.
|
Instantiates and returns the i-th form instance in a formset.
|
||||||
|
@ -184,6 +192,7 @@ class BaseFormSet(object):
|
||||||
auto_id=self.auto_id,
|
auto_id=self.auto_id,
|
||||||
prefix=self.add_prefix('__prefix__'),
|
prefix=self.add_prefix('__prefix__'),
|
||||||
empty_permitted=True,
|
empty_permitted=True,
|
||||||
|
**self.get_form_kwargs(None)
|
||||||
)
|
)
|
||||||
self.add_fields(form, None)
|
self.add_fields(form, None)
|
||||||
return form
|
return form
|
||||||
|
|
|
@ -238,6 +238,8 @@ pre-filled, and is also used to determine how many forms are required. You
|
||||||
will probably never need to override either of these methods, so please be
|
will probably never need to override either of these methods, so please be
|
||||||
sure you understand what they do before doing so.
|
sure you understand what they do before doing so.
|
||||||
|
|
||||||
|
.. _empty_form:
|
||||||
|
|
||||||
``empty_form``
|
``empty_form``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -533,6 +535,43 @@ default fields/attributes of the order and deletion fields::
|
||||||
<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>
|
<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>
|
||||||
<tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr>
|
<tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr>
|
||||||
|
|
||||||
|
Passing custom parameters to formset forms
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
Sometimes your form class takes custom parameters, like ``MyArticleForm``.
|
||||||
|
You can pass this parameter when instantiating the formset::
|
||||||
|
|
||||||
|
>>> from django.forms.formsets import BaseFormSet
|
||||||
|
>>> from django.forms.formsets import formset_factory
|
||||||
|
>>> from myapp.forms import ArticleForm
|
||||||
|
|
||||||
|
>>> class MyArticleForm(ArticleForm):
|
||||||
|
... def __init__(self, *args, **kwargs):
|
||||||
|
... self.user = kwargs.pop('user')
|
||||||
|
... super(MyArticleForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
>>> ArticleFormSet = formset_factory(MyArticleForm)
|
||||||
|
>>> formset = ArticleFormSet(form_kwargs={'user': request.user})
|
||||||
|
|
||||||
|
The ``form_kwargs`` may also depend on the specific form instance. The formset
|
||||||
|
base class provides a ``get_form_kwargs`` method. The method takes a single
|
||||||
|
argument - the index of the form in the formset. The index is ``None`` for the
|
||||||
|
:ref:`empty_form`::
|
||||||
|
|
||||||
|
>>> from django.forms.formsets import BaseFormSet
|
||||||
|
>>> from django.forms.formsets import formset_factory
|
||||||
|
|
||||||
|
>>> class BaseArticleFormSet(BaseFormSet):
|
||||||
|
... def get_form_kwargs(self, index):
|
||||||
|
... kwargs = super(BaseArticleFormSet, self).get_form_kwargs(index)
|
||||||
|
... kwargs['custom_kwarg'] = index
|
||||||
|
... return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
.. versionadded:: 1.9
|
||||||
|
|
||||||
|
The ``form_kwargs`` argument was added.
|
||||||
|
|
||||||
Using a formset in views and templates
|
Using a formset in views and templates
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,12 @@ class SplitDateTimeForm(Form):
|
||||||
SplitDateTimeFormSet = formset_factory(SplitDateTimeForm)
|
SplitDateTimeFormSet = formset_factory(SplitDateTimeForm)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomKwargForm(Form):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.custom_kwarg = kwargs.pop('custom_kwarg')
|
||||||
|
super(CustomKwargForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class FormsFormsetTestCase(SimpleTestCase):
|
class FormsFormsetTestCase(SimpleTestCase):
|
||||||
|
|
||||||
def make_choiceformset(self, formset_data=None, formset_class=ChoiceFormSet,
|
def make_choiceformset(self, formset_data=None, formset_class=ChoiceFormSet,
|
||||||
|
@ -114,6 +120,37 @@ class FormsFormsetTestCase(SimpleTestCase):
|
||||||
self.assertFalse(formset.is_valid())
|
self.assertFalse(formset.is_valid())
|
||||||
self.assertFalse(formset.has_changed())
|
self.assertFalse(formset.has_changed())
|
||||||
|
|
||||||
|
def test_form_kwargs_formset(self):
|
||||||
|
"""
|
||||||
|
Test that custom kwargs set on the formset instance are passed to the
|
||||||
|
underlying forms.
|
||||||
|
"""
|
||||||
|
FormSet = formset_factory(CustomKwargForm, extra=2)
|
||||||
|
formset = FormSet(form_kwargs={'custom_kwarg': 1})
|
||||||
|
for form in formset:
|
||||||
|
self.assertTrue(hasattr(form, 'custom_kwarg'))
|
||||||
|
self.assertEqual(form.custom_kwarg, 1)
|
||||||
|
|
||||||
|
def test_form_kwargs_formset_dynamic(self):
|
||||||
|
"""
|
||||||
|
Test that form kwargs can be passed dynamically in a formset.
|
||||||
|
"""
|
||||||
|
class DynamicBaseFormSet(BaseFormSet):
|
||||||
|
def get_form_kwargs(self, index):
|
||||||
|
return {'custom_kwarg': index}
|
||||||
|
|
||||||
|
DynamicFormSet = formset_factory(CustomKwargForm, formset=DynamicBaseFormSet, extra=2)
|
||||||
|
formset = DynamicFormSet(form_kwargs={'custom_kwarg': 'ignored'})
|
||||||
|
for i, form in enumerate(formset):
|
||||||
|
self.assertTrue(hasattr(form, 'custom_kwarg'))
|
||||||
|
self.assertEqual(form.custom_kwarg, i)
|
||||||
|
|
||||||
|
def test_form_kwargs_empty_form(self):
|
||||||
|
FormSet = formset_factory(CustomKwargForm)
|
||||||
|
formset = FormSet(form_kwargs={'custom_kwarg': 1})
|
||||||
|
self.assertTrue(hasattr(formset.empty_form, 'custom_kwarg'))
|
||||||
|
self.assertEqual(formset.empty_form.custom_kwarg, 1)
|
||||||
|
|
||||||
def test_formset_validation(self):
|
def test_formset_validation(self):
|
||||||
# FormSet instances can also have an error attribute if validation failed for
|
# FormSet instances can also have an error attribute if validation failed for
|
||||||
# any of the forms.
|
# any of the forms.
|
||||||
|
|
Loading…
Reference in New Issue