111

Why would I ever use save(commit=False) instead of just creating a form object from the ModelForm subclass and running is_valid() to validate both the form and model?

In other words, what is save(commit=False) for?

If you don't mind, could you guys provide hypothetical situations where this might be useful?

Vaibhav Mule
  • 4,771
  • 3
  • 34
  • 52
sgarza62
  • 5,500
  • 7
  • 41
  • 61

7 Answers7

134

That's useful when you get most of your model data from a form, but you need to populate some null=False fields with non-form data.

Saving with commit=False gets you a model object, then you can add your extra data and save it.

This is a good example of that situation.

Muhammmed Nihad
  • 79
  • 1
  • 1
  • 7
dokkaebi
  • 8,684
  • 3
  • 39
  • 58
  • But then if this gets you a model object, how does it differ from assigning an previously instantiated object and assigning it to the ModelForm? (i.e `form = forms.SampleForm(instance = models.Sample)`) – OzzyTheGiant Jul 18 '18 at 15:13
  • Do you need `commit=False` if you are processing your form in a `CBV` with `def form_valid`? Can you just use `form.instance.[field]` to update? – alias51 Jan 09 '20 at 11:28
  • Let's go to 100 :) – dani herrera May 27 '20 at 16:00
48

Here it is the answer (from docs):

# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)

# Create, but don't save the new author instance.
>>> new_author = f.save(commit=False)

The most common situation is to get the instance from form but only 'in memory', not in database. Before save it you want to make some changes:

# Modify the author in some way.
>>> new_author.some_field = 'some_value'

# Save the new instance.
>>> new_author.save()
dani herrera
  • 44,444
  • 7
  • 103
  • 165
  • 1
    Do you need `commit=False` if you are processing your form in a `CBV` with `def form_valid`? Can you just use `form.instance.[field]` to update? – alias51 Jan 09 '20 at 11:29
22

From the Django docs:

This save() method accepts an optional commit keyword argument, which accepts either True or False. 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. commit is True by default.

It seems that save(commit=False) does create a model instance, which it returns to you. Which is neat for some post processing before actually saving it!

Muhammmed Nihad
  • 79
  • 1
  • 1
  • 7
A.J.Rouvoet
  • 1,154
  • 1
  • 11
  • 27
10

As a "real example", consider a user model where the email address and the username are always the same, and then you could overwrite your ModelForm's save method like:

class UserForm(forms.ModelForm):
    ...
    def save(self):
        # Sets username to email before saving
        user = super(UserForm, self).save(commit=False)
        user.username = user.email
        user.save()
        return user

If you didn't use commit=False to set the username to the email address, you'd either have to modify the user model's save method, or save the user object twice (which duplicates an expensive database operation.)

Mark Chackerian
  • 19,405
  • 6
  • 103
  • 97
  • Do you need `commit=False` if you are processing your form in a `CBV` with `def form_valid`? Can you just use `form.instance.[field]` to update? – alias51 Jan 09 '20 at 11:29
2

The basic thing here I understand is that it changes from a 'form' instance to a particular 'model' instance in view.

Let suppose I want to post an answer like this in StackOverflow. The code would be like this:

# Create a form instance with POST data.
>>> form_data = AnswerForm(request.POST)

# Create, but don't save the new answer instance.
>>> Answer = form_data.save(commit=False)

So now we have to add the owner of this answer and save this in our database in the view page like this:

>>> Answer.owner = request.user

>>> Answer.save()

So like this, we can add the owner of this answer which we cannot do like form_data.owner = request.user in the view page and also not in the form class.

So basically, it changes from 'form' instance to 'model' instance and then lets you to modify the data and save it.

Muhammmed Nihad
  • 79
  • 1
  • 1
  • 7
Dev shah
  • 21
  • 1
  • 1
1
            form = AddAttachmentForm(request.POST, request.FILES)
            if form.is_valid():
                attachment = form.save(commit=False)
                attachment.user = student
                attachment.attacher = self.request.user
                attachment.date_attached = timezone.now()
                attachment.competency = competency
                attachment.filename = request.FILES['attachment'].name
                if attachment.filename.lower().endswith(('.png','jpg','jpeg','.ai','.bmp','.gif','.ico','.psd','.svg','.tiff','.tif')):
                    attachment.file_type = "image"
                if attachment.filename.lower().endswith(('.mp4','.mov','.3g2','.avi','.flv','.h264','.m4v','.mpg','.mpeg','.wmv')):
                    attachment.file_type = "video"
                if attachment.filename.lower().endswith(('.aif','.cda','.mid','.midi','.mp3','.mpa','.ogg','.wav','.wma','.wpl')):
                    attachment.file_type = "audio"
                if attachment.filename.lower().endswith(('.csv','.dif','.ods','.xls','.tsv','.dat','.db','.xml','.xlsx','.xlr')):
                    attachment.file_type = "spreasheet"
                if attachment.filename.lower().endswith(('.doc','.pdf','.rtf','.txt')):
                    attachment.file_type = "text"
                attachment.save()

here is my example of using save(commit=False). I wanted to check what type of file a user uploaded before saving it to the database. I also wanted to get the date it was attached since that field was not in the form.

1

In simple words, here we update the form object and let them know that don't save the values in the database right now, we might change some input with instance and then use .save() to save all values in the database.

This gives us the flexibility to get all values from the HTML form and customize them according to our requirement and then save the instance.