Search code examples
pythondjangodjango-models

with regard to form.save(commit=False), what am I missing?


As explained in many posts like the following: Why do we use "form.save(commit=False)" in Django-views? "The main use case is if you have a ModelForm that doesn't contain all the required fields of a model. You need to save this form in the database, but because you didn't give it all the required fields, you will get an error.." which is all good and dandy but why not just complete all the instance fields and then call the regular save? In the bellow example:

# Creates a Dog class with all fields as mandatory:
class Dog(models.Model):
    name = models.CharField(max_length=50)
    race = models.CharField(max_length=50)
    age = models.PositiveIntegerField()

# Creates a ModelForm with only name and age:
class DogForm(forms.ModelForm):
    class Meta:
        model = Dog
        fields = ['name', 'age']

# In your view use this form:
def dog_view(request):
    ...
    form = DogForm(request.POST or None)
    # If the form is valid we need to add a race, otherwise we will get an error:
    if form.is_valid():
        dog = form.save(commit=False)
        # Define the race here:
        dog.race = 'Labrador retriever'
        # And then do the regular save to push the change in the database:
        dog.save()
        ...

why not just say:

    form = DogForm(request.POST or None)
        # Define the race:
        dog.race = 'Labrador retriever'
        if form.is_valid():
            # Do the regular save:
            dog.save()

What am I missing here?


Solution

  • which is all good and dandy but why not just complete all the instance fields and then call the regular save?

    That is possible. In fact I recommend this approach in an article I wrote [django-antipatterns].

    But I would advise to let the form handle the logic, like:

    def dog_view(request):
        # …
        form = DogForm(request.POST, request.FILES)
        if form.is_valid():
            form.instance.race = 'Labrador retriever'
            form.save()
        # …

    The main reason for this is that Django forms also can save many-to-many fields, and thus make more database queries. By using dog.save() you save the dog instance, and in this case, that is fine. But if you have a ManyToManyField named owners for example. It will thus no longer save the owners of the dog.


    Note: While most forms do not process media files, it is probably better to pass request.FILES [Django-doc] to the form anyway, such that if you later add an extra media field, all views that use the form will indeed handle the files properly.