Anonymous avatar Anonymous committed a7a99e2

newforms-admin: Fixed #7230 -- Added a save_m2m method to BaseModelFormSet when commit=False is passed to save. Thanks Books Travis for the original report.

Comments (0)

Files changed (3)


         """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):
                 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):
             if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
             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):


 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


     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
 __test__ = {'API_TESTS': """
+>>> from datetime import date
 >>> from django.newforms.models import modelformset_factory
 >>> qs = Author.objects.all()
 [<Author: Walt Whitman>]
+Test the behavior of commit=False and save_m2m
+>>> meeting = AuthorMeeting.objects.create(
+>>> 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()
+>>> instances =
+>>> for instance in instances:
+...     instance.created =
+>>> 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
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.