The documents seem pretty solid that this is true.
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
And I specifically refer to this section:
Another side effect of using commit = False is when your model has a many-to-many relationship to another model. If your model has a many-to-many relationship and you specify commit = False when you save the form, Django cannot immediately save the form data for the many-to-many relationship. This is because it is not possible to store many-to-many data for an instance until the instance exists in the database.
To work around this problem, every time you save the form with commit = False, Django adds the save_m2m () method to your subclass of ModelForm. After you manually saved the instance created by the form, you can call save_m2m () to save the many-to-many form data.
I am new to django and came across this info yesterday.
However, I have a view where I do not call save_m2m (), but it actually saves m2m data.
Here is my view:
class SubscriberCreateView(AuthCreateView): model = Subscriber template_name = "forms/app.html" form_class = SubscriberForm success_url = "/app/subscribers/" def get_form_kwargs(self): kwargs = super(SubscriberCreateView, self).get_form_kwargs() kwargs.update({'user': self.request.user}) return kwargs def form_valid(self, form): self.object = form.save(commit=False) self.object.user = self.request.user try: self.object.full_clean() except ValidationError: form._errors["email"] = ErrorList([u"This subscriber email is already in your account."]) return super(SubscriberCreateView, self).form_invalid(form) return super(SubscriberCreateView, self).form_valid(form)
My model:
class Subscriber(models.Model): STATUS_CHOICES = ( (1, ('Subscribed')), (2, ('Unsubscribed')), (3, ('Marked as Spam')), (4, ('Bounced')), (5, ('Blocked')), (6, ('Disabled')), ) user = models.ForeignKey(User) status = models.IntegerField(('status'), choices=STATUS_CHOICES, default=1) email = models.EmailField() subscriber_list = models.ManyToManyField('SubscriberList') first_name = models.CharField(max_length=70, blank=True) last_name = models.CharField(max_length=70, blank=True) phone = models.CharField(max_length=20, blank=True) facebook_id = models.CharField(max_length=40, blank=True) twitter_id = models.CharField(max_length=40, blank=True) address1 = models.CharField(max_length=100, blank=True) address2 = models.CharField(max_length=100, blank=True) postcode = models.CharField(max_length=10, blank=True) city = models.CharField(max_length=30, blank=True) country = models.CharField(max_length=30, blank=True) date_joined = models.DateTimeField(auto_now_add=True) date_updated = models.DateTimeField(auto_now=True) class Meta: unique_together = ( ('user', 'email',), ) def __unicode__(self): return self.email
My form:
class SubscriberForm(ModelForm): def __init__(self, user, *args, **kwargs): super (SubscriberForm, self).__init__(*args, **kwargs) self.fields['subscriber_list'].queryset = SubscriberList.objects.filter(user=user) class Meta: model = Subscriber exclude = ('user', 'facebook_id', 'twitter_id')
Why does my presentation work? (which means that the m2m ratio of one of the fields in the form is actually preserved when processing the form.)