Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - form_valid() vs save()

In django forms, for saving other data I usually use form_valid() but as I can also use save() method of formclass.

Today I overrided save() instead of form_valid() and I got problem with my manytomanyfield.

When using , the values of manytomanyfield are not saved but when I use form_valid() they start saving. Can anybody tell me the reason and what are the differences between both, which is the most convenient method and in what situation?

Here is my overriding of save() method:

class ProductCreateForm(forms.ModelForm):
    sizes = make_ajax_field(ProductCreateModel,'sizes','sizes')
    colours = make_ajax_field(ProductCreateModel,'colours','colours')

    class Meta:
        model = ProductCreateModel

        fields = ('title','category',
                    'regions',)

    def __init__(self,*args,**kwargs):
        self.request = kwargs.pop("request")
        super(ProductCreateForm, self).__init__(*args, **kwargs)

    def save(self):
        product = super(ProductCreateForm, self).save(commit=False)
        user =  self.request.user

        product.location = user.user_location
        product.save()
        return product

When I override form_valid() method:

   def get_form_kwargs(self):
       kwargs = super(ProductCreateView,self).get_form_kwargs()
       kwargs.update({'request':self.request})
       return kwargs

   def form_valid(self, form):
       product = form.save(commit=False)
       user =  self.request.user
       form.instance.user = user
       form.instance.location = user.user_location
       form.save()
       return super(ProductCreateView, self).form_valid(form)

sizes,colours and regions are m2m fields, as I mentioned when I overrides save() values of m2m not get saved but when I overrides form_valid they start saving.

like image 553
Pankaj Sharma Avatar asked Jul 04 '18 11:07

Pankaj Sharma


2 Answers

If you save a form with commit=False, you must call the form's save_m2m method to save the many-to-many data. See the docs for more info.

If you decide to use the form_valid method, I would change the following things:

  • update the instance returned by form.save() and save it, instead of calling form.save() again.
  • explicitly call form.save_m2m()
  • return a redirect response instead of calling super().form_valid() (which will save the form again)

Putting that together, you get:

def form_valid(self, form):
    product = form.save(commit=False)
    product.user =  self.request.user
    product.location.location = user.user_location
    product.save()
    form.save_m2m()
    return redirect('/success-url/')
like image 69
Alasdair Avatar answered Oct 30 '22 17:10

Alasdair


About your problem with manytomany i guess is the order they do things... Form > Admin > Models, when you use form_valid is the first thing they do before check other things in chain, while using save is the last, maybe can be because or other things too...

The best way is always use form_valid instead of raw save

form_valid first check the Clean function if there is any native validations errors or custom validations and only then save your models

save just save it without validate then with your form with your validations

Example

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject:
            # Only do something if both fields are valid so far.
            if "help" not in subject:
                raise forms.ValidationError(
                    "Did not send for 'help' in the subject despite "
                    "CC'ing yourself."
                )

Source: https://docs.djangoproject.com/en/2.0/ref/forms/validation/

like image 25
Diego Vinícius Avatar answered Oct 30 '22 17:10

Diego Vinícius