mirror of
https://github.com/django/django.git
synced 2024-12-23 01:25:58 +00:00
Fixed #18166 -- Added form_kwargs support to formsets.
By specifying form_kwargs when instantiating the formset, or overriding the `get_form_kwargs` method on a formset class, you can pass extra keyword arguments to the underlying `Form` instances. Includes tests and documentation update.
This commit is contained in:
parent
57dbc87ade
commit
238e2ac369
@ -54,13 +54,14 @@ class BaseFormSet(object):
|
||||
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, form_kwargs=None):
|
||||
self.is_bound = data is not None or files is not None
|
||||
self.prefix = prefix or self.get_default_prefix()
|
||||
self.auto_id = auto_id
|
||||
self.data = data or {}
|
||||
self.files = files or {}
|
||||
self.initial = initial
|
||||
self.form_kwargs = form_kwargs or {}
|
||||
self.error_class = error_class
|
||||
self._errors = None
|
||||
self._non_form_errors = None
|
||||
@ -139,9 +140,16 @@ class BaseFormSet(object):
|
||||
Instantiate forms at first property access.
|
||||
"""
|
||||
# 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
|
||||
|
||||
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):
|
||||
"""
|
||||
Instantiates and returns the i-th form instance in a formset.
|
||||
@ -184,6 +192,7 @@ class BaseFormSet(object):
|
||||
auto_id=self.auto_id,
|
||||
prefix=self.add_prefix('__prefix__'),
|
||||
empty_permitted=True,
|
||||
**self.get_form_kwargs(None)
|
||||
)
|
||||
self.add_fields(form, None)
|
||||
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
|
||||
sure you understand what they do before doing so.
|
||||
|
||||
.. _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-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
|
||||
--------------------------------------
|
||||
|
||||
|
@ -57,6 +57,12 @@ class SplitDateTimeForm(Form):
|
||||
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):
|
||||
|
||||
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.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):
|
||||
# FormSet instances can also have an error attribute if validation failed for
|
||||
# any of the forms.
|
||||
|
Loading…
Reference in New Issue
Block a user