summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/newforms/models.py10
-rw-r--r--docs/modelforms.txt4
-rw-r--r--tests/modeltests/model_formsets/models.py44
3 files changed, 57 insertions, 1 deletions
diff --git a/django/newforms/models.py b/django/newforms/models.py
index 845ba975bb..43e2978ba8 100644
--- a/django/newforms/models.py
+++ b/django/newforms/models.py
@@ -331,6 +331,12 @@ class BaseModelFormSet(BaseFormSet):
"""Saves model instances for every form, adding and changing instances
as necessary, and returns the list of instances.
"""
+ if not commit:
+ self.saved_forms = []
+ def save_m2m():
+ for form in self.saved_forms:
+ form.save_m2m()
+ self.save_m2m = save_m2m
return self.save_existing_objects(commit) + self.save_new_objects(commit)
def save_existing_objects(self, commit=True):
@@ -353,6 +359,8 @@ class BaseModelFormSet(BaseFormSet):
if form.changed_data:
self.changed_objects.append((obj, form.changed_data))
saved_instances.append(self.save_existing(form, obj, commit=commit))
+ if not commit:
+ self.saved_forms.append(form)
return saved_instances
def save_new_objects(self, commit=True):
@@ -365,6 +373,8 @@ class BaseModelFormSet(BaseFormSet):
if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
continue
self.new_objects.append(self.save_new(form, commit=commit))
+ if not commit:
+ self.saved_forms.append(form)
return self.new_objects
def add_fields(self, form, index):
diff --git a/docs/modelforms.txt b/docs/modelforms.txt
index 5cff11fdeb..9c06bc409d 100644
--- a/docs/modelforms.txt
+++ b/docs/modelforms.txt
@@ -454,7 +454,9 @@ model instances without any database interaction::
... instance.save()
This gives you the ability to attach data to the instances before saving them
-to the database.
+to the database. If your formset contains a ``ManyToManyField`` you will also
+need to make a call to ``formset.save_m2m()`` to ensure the many-to-many
+relationships are saved properly.
Limiting the number of objects editable
---------------------------------------
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
index 93519a4421..84265450fc 100644
--- a/tests/modeltests/model_formsets/models.py
+++ b/tests/modeltests/model_formsets/models.py
@@ -13,9 +13,19 @@ class Book(models.Model):
def __unicode__(self):
return self.title
+class AuthorMeeting(models.Model):
+ name = models.CharField(max_length=100)
+ authors = models.ManyToManyField(Author)
+ created = models.DateField(editable=False)
+
+ def __unicode__(self):
+ return self.name
+
__test__ = {'API_TESTS': """
+>>> from datetime import date
+
>>> from django.newforms.models import modelformset_factory
>>> qs = Author.objects.all()
@@ -162,6 +172,40 @@ True
>>> formset.save()
[<Author: Walt Whitman>]
+Test the behavior of commit=False and save_m2m
+
+>>> meeting = AuthorMeeting.objects.create(created=date.today())
+>>> meeting.authors = Author.objects.all()
+
+# create an Author instance to add to the meeting.
+>>> new_author = Author.objects.create(name=u'John Steinbeck')
+
+>>> AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, extra=1, can_delete=True)
+>>> data = {
+... 'form-TOTAL_FORMS': '2', # the number of forms rendered
+... 'form-INITIAL_FORMS': '1', # the number of forms with initial data
+... 'form-MAX_FORMS': '0', # the max number of forms
+... 'form-0-id': '1',
+... 'form-0-name': '2nd Tuesday of the Week Meeting',
+... 'form-0-authors': [2, 1, 3, 4],
+... 'form-1-name': '',
+... 'form-1-authors': '',
+... 'form-1-DELETE': '',
+... }
+>>> formset = AuthorMeetingFormSet(data=data, queryset=AuthorMeeting.objects.all())
+>>> formset.is_valid()
+True
+>>> instances = formset.save(commit=False)
+>>> for instance in instances:
+... instance.created = date.today()
+... instance.save()
+>>> formset.save_m2m()
+>>> instances[0].authors.all()
+[<Author: Charles Baudelaire>, <Author: Walt Whitman>, <Author: Paul Verlaine>, <Author: John Steinbeck>]
+
+# delete the author we created to allow later tests to continue working.
+>>> new_author.delete()
+
Test the behavior of max_num with model formsets. It should properly limit
the queryset to reduce the amount of objects being pulled in when not being
used.