Django: object has no attribute ‘save’

When using model forms you can save values entered into a form by calling the form’s save() method as shown below.

if form.is_valid():
    form.save()

If you are not using model forms an attempt to call form.save() results in an error such as:

The save() method works with model forms because there is a model associated with it so Django knows what to save and how. When you are using simple forms the link between the form and the model is not implicitly available so you have to explicitly tell Django what form values you want to save. The following example borrowed from this project shows how this can be achieved.

The goal of the example project is to display the distance between two post codes either in kilometres or miles. The model therefore has three fields, a starting post code, an ending post code and the measurement units.

models.py

from django.db import models

class Postcode(models.Model):
    start_postcode = models.CharField(max_length=4)
    end_postcode = models.CharField(max_length=4)
    result_measurement_unit = models.CharField(max_length=4)

Similar to the model the form also has three fields and named similar to the model with the exception that the field used to determine the measure units is called distance_unit.

forms.py

from django import forms

MEASUREMENT_UNITS = [
    ('KM', 'km'),
    ('M', 'miles'),
] 

class PostcodeForm(forms.Form):
    start_postcode = forms.CharField(max_length=4)
    end_postcode = forms.CharField(max_length=4)
    distance_unit = forms.ChoiceField(choices=MEASUREMENT_UNITS, label='Distance in', widget=forms.RadioSelect(choices=MEASUREMENT_UNITS), initial='KM'  )

The logic to save the form values to the database are added to views.py.

views.py

if request.method == "POST":

        form = PostcodeForm(request.POST)

        if form.is_valid():
            
            cd = form.cleaned_data
            
            pc = Postcode(
                start_postcode = cd['start_postcode'],
                end_postcode = cd['end_postcode'],
                result_measurement_unit = cd['distance_unit']
            )
            
            pc.save()
            ...

Stepping through the view.py code:

Line 1: Checks that this is a post request

Line 3: A reference to the form is obtained and stored in the variable form.

Line 5: The form is tested to ensure the values entered are valid.

Line 7: The form values are obtained using form.cleaned data and stored in the cd variable.

Line 9: A new Postcode object, defined in the models.py file is created and it’s attributes are populated using the values from the form.

Line 15: The save method of the object is called.

Acknowledgements

My thanks to the author and contributes of the pgeocode library which was used in the example project.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.