[Fixed]-Is save_m2m() required in the Django forms save() method when commit=False?

9👍

One of the parent classes is performing the full save of the model object and its m2m relations. I can’t know for sure because I don’t have the declaration of AuthCreateView, but the naming convention indicates that it stems from “CreateView”. If so, the inheritance of your View goes like this, SubscriberCreateView -> AuthCreateView -> CreateView -> BaseCreateView -> ModelFormMixin. ModelFormMixin has a form_valid() method that you are (probably) calling with super().

Here’s the entire method from Django 1.4:

def form_valid(self, form):
    self.object = form.save()
    return super(ModelFormMixin, self).form_valid(form)

So there you have it. However, let me point some potential confusion. @Wogan is astute when pointing out that you don’t save your object. The way your code stands, you use your unsaved model instance for validation and then it is discarded because ModelFormMixin re-assigns self.object.

  1. This means self.object might not be what you expect if you access it later on.
  2. You lose the self.object.user information. (Because user is a required field on the Model and you exclude it in the Form, I would expect the save() to fail. So the parent AuthCreateView must be doing something. Of course it might be handling the entire save() and never hitting ModelFormMixin at all.)

To avoid this confusion, simply don’t assign your instance to self.object. Perhaps: validate_obj = form.save(commit=False)

1👍

I noticed that you do not actually save the object that you get via your form.save(commit=False) call. So it appears to me that your data is actually being saved somewhere else – probably in AuthCreateView‘s form_valid method (or another ancestor class). This would explain why the many-to-many objects are being correctly saved.

👤Wogan

0👍

It’s a simple answer if we know how to use save(commit=False).

save method with commit=False does not change your database:

“If you call save() with commit=False, then it will return an object that hasn’t yet been saved to the database. In this case, it’s up to you to call save() on the resulting model instance. This is useful if you want to do custom processing on the object before saving it, or if you want to use one of the specialized model saving options.”

So in the case of many-to-many relationship it’s impossible to save m2m data without saving an object to a database. Usual save( commit=False ) can change an object as you saved part of data (not data that assign to m2m relationship). You really can’t store m2m relationship in memory only.

If you want to work with that m2m data of model object after save(commit=False) save_m2m() required. You should do this when you use save(commit=False) otherwise you may get an intermediate object after save(commit=False) that does not correspond to your database (or database model) properly. Sometimes it may be normal (if you don’t touch the data which are involved in processing of model’s m2m part). To restore the conformity call save_m2m any time you call save(commit=False).

View the save_m2m implementation:

def save_m2m():
    cleaned_data = form.cleaned_data
    for f in opts.many_to_many:
        if fields and f.name not in fields:
            continue
        if f.name in cleaned_data:
            f.save_form_data(instance, cleaned_data[f.name])

Leave a comment