diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 9cdd05198c..dcd2f017e7 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -122,7 +122,7 @@ class BaseFormSet(StrAndUnicode): if self.is_bound: defaults['data'] = self.data defaults['files'] = self.files - if self.initial: + if self.initial and not 'initial' in kwargs: try: defaults['initial'] = self.initial[i] except IndexError: diff --git a/django/forms/models.py b/django/forms/models.py index b65f067846..15d50419d4 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -418,6 +418,7 @@ class BaseModelFormSet(BaseFormSet): def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, queryset=None, **kwargs): self.queryset = queryset + self.initial_extra = kwargs.pop('initial', None) defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix} defaults.update(kwargs) super(BaseModelFormSet, self).__init__(**defaults) @@ -448,6 +449,12 @@ class BaseModelFormSet(BaseFormSet): kwargs['instance'] = self._existing_object(pk) if i < self.initial_form_count() and not kwargs.get('instance'): kwargs['instance'] = self.get_queryset()[i] + if i >= self.initial_form_count() and self.initial_extra: + # Set initial values for extra forms + try: + kwargs['initial'] = self.initial_extra[i-self.initial_form_count()] + except IndexError: + pass return super(BaseModelFormSet, self)._construct_form(i, **kwargs) def get_queryset(self): @@ -674,7 +681,7 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None, class BaseInlineFormSet(BaseModelFormSet): """A formset for child objects related to a parent.""" def __init__(self, data=None, files=None, instance=None, - save_as_new=False, prefix=None, queryset=None): + save_as_new=False, prefix=None, queryset=None, **kwargs): from django.db.models.fields.related import RelatedObject if instance is None: self.instance = self.fk.rel.to() @@ -687,7 +694,7 @@ class BaseInlineFormSet(BaseModelFormSet): queryset = self.model._default_manager qs = queryset.filter(**{self.fk.name: self.instance}) super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, - queryset=qs) + queryset=qs, **kwargs) def initial_form_count(self): if self.save_as_new: diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index ea3e9a7fc9..9b3c219d31 100644 --- a/docs/releases/1.4.txt +++ b/docs/releases/1.4.txt @@ -556,6 +556,12 @@ Django 1.4 also includes several smaller improvements worth noting: * The MySQL database backend can now make use of the savepoint feature implemented by MySQL version 5.0.3 or newer with the InnoDB storage engine. +* It is now possible to pass initial values to the model forms that are part of + both model formsets and inline model formset as returned from factory + functions ``modelformset_factory`` and ``inlineformset_factory`` respectively + just like with regular formsets. However, initial values only apply to extra + forms i.e. those which are not bound to an existing model instance. + Backwards incompatible changes in 1.4 ===================================== diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index 37c3067f55..b524c24ad2 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -53,6 +53,8 @@ Formsets can also be indexed into, which returns the corresponding form. If you override ``__iter__``, you will need to also override ``__getitem__`` to have matching behavior. +.. _formsets-initial-data: + Using initial data with a formset --------------------------------- diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index 832a3acff0..cd1f43ae49 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -617,6 +617,17 @@ exclude:: >>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',)) +Providing initial values +------------------------ + +.. versionadded:: 1.4 + +As with regular formsets, it is possible to :ref:`specify initial data +` for forms in the formset by specifying an ``initial`` +parameter when instantiating the model formset class returned by +``modelformset_factory``. However, with model formsets the initial values only +apply to extra forms, those which are not bound to an existing object instance. + .. _saving-objects-in-the-formset: Saving objects in the formset diff --git a/tests/regressiontests/model_formsets_regress/tests.py b/tests/regressiontests/model_formsets_regress/tests.py index 35a794aa78..613d77616c 100644 --- a/tests/regressiontests/model_formsets_regress/tests.py +++ b/tests/regressiontests/model_formsets_regress/tests.py @@ -204,6 +204,17 @@ class InlineFormsetTests(TestCase): ["", ""] ) + def test_initial_data(self): + user = User.objects.create(username="bibi", serial=1) + UserSite.objects.create(user=user, data=7) + FormSet = inlineformset_factory(User, UserSite, extra=2) + + formset = FormSet(instance=user, initial=[{'data': 41}, {'data': 42}]) + self.assertEqual(formset.forms[0].initial['data'], 7) + self.assertEqual(formset.extra_forms[0].initial['data'], 41) + self.assertTrue(u'value="42"' in formset.extra_forms[1].as_p()) + + class FormsetTests(TestCase): def test_error_class(self): ''' @@ -230,6 +241,14 @@ class FormsetTests(TestCase): self.assertTrue(isinstance(form.errors, ErrorDict)) self.assertTrue(isinstance(form.non_field_errors(), ErrorList)) + def test_initial_data(self): + User.objects.create(username="bibi", serial=1) + Formset = modelformset_factory(User, extra=2) + formset = Formset(initial=[{'username': u'apollo11'}, {'username': u'apollo12'}]) + self.assertEqual(formset.forms[0].initial['username'], "bibi") + self.assertEqual(formset.extra_forms[0].initial['username'], "apollo11") + self.assertTrue(u'value="apollo12"' in formset.extra_forms[1].as_p()) + class CustomWidget(forms.CharField): pass