Abstracted form to save an object--which save methods should I override?

The overall picture is that I am making a form to create/edit a model object that abstracts away some model details for the person filling out the form. So, somewhere I need to take the form fields and do a little extra work to create the actual object in the database. But, I am not sure where I should do this.

From my current understanding, there seem to be many places where I can create and save the object.

The form class (a django.forms.ModelForm) has a save(self, commit=True) that is not being called. That seems like a place where I would save an object. One time I did try overriding this and calling it from form_valid but I got errors.

The view (a django.views.generic.edit.FormView) has form_valid(self, form) that is getting called and where I can access the information I need to create the object.

The model (a django.models.Model) has a save(self, force_insert=False, force_update=False, using=None, update_fields=None) which is not getting called either. But then it looks like I can also override a create method, or I could have one in an object manager.

So the question I have is mainly, what classes and methods might I use to create an object out of an abstracted form?

I also am having some trouble finding a reference that tells me what methods each of these classes has available to override and what their arguments are. Right now I am using the source code, but because some classes inherit from several others, it gets frustrating to find all the methods. I typically find the official django resources to be very useful, but usually I need to modify what is done to fit my needs which requires more information than they provide (for example, “Creating forms from models” and “Working with forms” don’t even mention the form_valid method. Where can I find more detailed information?

So the fundamental class is a Form. A ModelForm is an extension of a Form when the Form is a close match to a Model.

If you’re needing to do work on more than one or two fields, I would recommend not using a ModelForm, and going with the basic Form itself.

(Side note: The Classy Class Based View site is an excellent view into how the generic views work and what’s available to use when working with them. I recommend it highly.)

Anyway, if you look at the Django docs for FormView, the second example down shows an override of the form_valid function. That seems like a good place where you can build your custom model and save it after verifying that it’s valid.

The form_valid method is passed the form as a parameter. That means you have access to the cleaned_data attribute on the form to access the individual fields.

Ken

1 Like

Thanks! The Classy Class Based View site is just what I wanted.

I think using a FormView would work, but I think in my case it would be a poor design choice since the form does closely follow the model, except for the need to change two fields from object selectors to text fields what will be used to create the objects. This way if I add new fields to the model (there will be several more in the future) I don’t need to also add them to the form.

Looking at the source code, the only method I can find that sounds like what I should overwrite is this save method in the BaseModelForm. But, it is not being called when I submit the form. This makes me think that it is not the method that I should be adding to.

I thought that maybe I should try overriding something on the model itself. I found this save method, but it is not being called either.

The form_valid method is being called, so I can save an instance there. But, I don’t know if that is the right place. The ModelForm saves an instance somewhere–I just can’t figure out where. And I don’t want to save two objects by accident.

Form_valid is the right place. If you look at the source, you’ll see that that’s where it’s calling form.save.

So yes, you can override that method - but without eventually calling super.

If I were doing this, my form_valid method would have something like this within it:

new_object = form.save(commit=False)
new_object.custom_field_1 = some_calculated_result()
new_object.custom_field_2 = 42
new_object.save()

(See the second example box in the save() method docs linked above.)

Ken

2 Likes

Just double checking, is this the form_valid for a FormView? That’s the one I have been using which doesn’t look like it calls form.save (it appears to only return an HttpResponseRedirect). Additionally, when I override with return super().form_valid(form) the ModelForm save method is not run. You mention “without calling super” and I wonder if this test I did does not work (the source of my initial confusion about how these various classes work together).

What I expected before I did a test and looked at the source code was form_valid to call the ModelForm save method, and that save method to call the Model save method or the Manager create method (and from what you share it sounds like this is right and I have a misunderstanding somewhere).

Close but not quite.

If you’re going the full CBV route, you’re using either (or both) CreateView and UpdateView. Both inherit from ModelFormMixin, which has a form_valid method defined, containing the form.save().

1 Like