Implementing Django ModelForms

Implementing Django ModelForms

In my previous posts, I showed you how to set up a Django project and host it on Heroku. But a page that only shows ‘Hello World!’ isn’t really all that useful, aside from demonstrating that you know how to configure your project to host it on the web. In this next series of posts I hope to show you how to configure a few basic elements of a website. By request, I’ll begin with a tutorial detailing how to implement a form that will capture and save entries as database records in Heroku for Django, using PostgreSQL!

 

In this tutorial, we will:

  1. Create a  model class
  2. Register our app with our site
  3. Update the database
  4. Test the model class
  5. Create a Form based on the model class
  6. Update the View to utilize the form
  7. Create the Index template to display the form and allow us to input data
  8. Update the Index template to display the previously entered form values

 

Step 0: Load Up Your IDE

This is not strictly necessary, but it will make life a lot easier. If you haven’t installed an IDE yet, I recommend PyCharm Community Edition by Jetbrains. You can find installation and setup instructions here.

 

Step 1: Creating a Model Class

Django models are what define the database schema. Django will interpret the models.py file and generate migration files. These files will be the SQL scripts that then get applied to the database. No manual SQL writing required!

Open the models.py file. In your project tree, you’ll find it in here: mysite/myapp/models.py.

django-forms-models-py

 

Add the Base Model

The model we’ll be adding will contain the details of a blog post. The database record will be uniquely identified by both the title and the slug (a slug is a term coined by the newspaper industry as a means of quickly identifying an article. In Django, it’s just a URL-friendly version of the title, in which the spaces have been replaced with hyphens). Even though we’re using a slug as our unique identifier, Django will still create an id column for us and increment it with each new object added, we just don’t have to declare it manually.

 

What Does it All Mean?

Let’s take a look at the Blog model class we just created. Each line, or element, inside Blog and above the save method represents a column in the database table. From line 11,  Django will create an element, or column, named ‘title’. It will be a string datatype with a max length of 100, and will be required. The parameter unique=True guarantees that each entry in this column will be unique within the table. The post_date element uses the default parameter, which we’ve set to assign the field the value of the current date and time, based on the timezone defined in the settings.py file.

We’ve also overridden the default save method in order to properly create the slug entry, which is required in our Blog model. This means that when the Blog object is saved, the value included in the title element will be ‘slugified’. The if statement simply checks whether this Blog object already has an id (that is, it’s been saved before, and this is a record update, in which case we don’t need a new slug since it should already have one), and if it doesn’t, it creates the slug and saves the record.

 

Let’s Make it Better

We can alter the model slightly to make it a little more human readable by adding default ordering to the meta and including a return output. The ordering will provide a default sort order of results when the database is queried. This can be overridden in the actual query, of course.

 

Step 2: Updating the Installed Apps List

Next, we need to tell our project about our app so Django knows where to check for model changes. This is done by adding our app to the INSTALLED_APPS section of the settings.py file.

Open mysite/settings.py and add ‘myapp’ to the list of INSTALLED_APPS.

django-forms-settings-py

 

Step 3: Updating the Database Schema

Now we can create the migration files and apply them to our database. Basically, Django looks at all models.py files it can find, and either creates or modifies the model objects in the database. Each migration file will list its migration dependencies, so try not to lose any of your old migrations!

This will create a new file in mysite/myapp/migrations named 0001_initial.py.

django_create_migrations

 

Uh Oh, I forgot to Add a Field to My Model!

Don’t worry. If you decide you need to make an adjustment before actually applying the migrations to your database but don’t want to manually edit the SQL inside the migration file, you have two options: 1) delete the latest migration file and run makemigrations again, or 2) simply run makemigrations again to generate a new migration file, which will list your previous migration file as a dependency. When you apply the migrations to your database, the migration files will be applied in order, so that your database matches your models.py configuration!

WARNING! Don’t delete a migration file that has already been applied to the database! This will cause your configuration to be out of sync with your migration and models.py files. Very messy.

 

Using a Local Database

If you’re running a local database, it’s a straightforward task to apply the migrations to the database. If you’re using Heroku, however, this won’t work properly. You can read more about that in this post.

Done!

 

Using a Remote Database

When applying migrations to a remote Heroku database, you’ll actually need to add the migration files to your repository and push them to Heroku, then perform the migration operation remotely using Heroku run.

NOTE: if you have more than one application, you’ll need to specify which application to apply the migrations to by appending ‘–app appname’ to the migrate command (where appname is the name of your Heroku application.)

Great! Your database should now have a table named Blog, which is ready for you to insert new records!

 

Step 4: Testing the Blog Model Class From the Shell

Django provides tools that allow us to add records to our database model via the shell. You’ll just need two things: the location of the models file, and the structure of the Blog table. Below, we import the Blog model from myapp.models, which tells the shell to look in the file mysite/myapp/models.py. Note that since we’re in the directory mysite when we run the shell command, we only need to specify the relative path to the models.py file.

Then, we create a variable b, which is an instance of the Blog class, and pass it some values: title and body. We can check the values of the Blog object by using ‘b.fieldName’. For example, ‘b.body’ returns ‘This is the body’, ‘b.post_date’ returns the datetime value recorded when we created the variable b. But wait! Why is b.slug returning an empty unicode string? That’s because we need to call the save() method of Blog before the slug gets created. Save the record, then try again, and you’ll see that the slug is the slugified title!

 

Now, make sure the record has been saved by returning a queryset of all objects (records) in the Blog model class (table). This is equivalent to select * from myapp_blog.

 

We can also add a filter to the query to return a single record, and even return the value of a specific field.

Glorious!

 

Step 5: Creating the Form Based on the Model Object

Add the file mysite/myapp/forms.py

django_form_forms-py

 

Open the file and add the following:

 

A ModelForm is tied directly to a Model. In this case, we’ve imported the model Blog from mysite/myapp/models.py (models.py and forms.py are in the same directory!), tied it to the form in the meta clause, and defined which fields to show! That’s all we need to do! The view will handle the actual transaction of saving the form entries into the database.

 

Step 6: Updating the Index View to Utilize Our New Form

Open up mysite/myapp/views.py and let’s set it up.

django-forms-views-py

Update your imports and replace your index view with the following:

There’s a lot going on here, so I’ll break it down.

  • First we import forms to the views.py file. As with models.py, forms.py is in the same directory as as views.py, so we don’t need to define the path.
  • Then we pass in the request object to the index view.
  • In the index view, we check to see what type of request it is. If this is a form submission, the request type will be POST, and there will be data to process. Otherwise, this is a page load request.
  • If this IS a POST, we want to set the form values from the POST variable. form = BlogForm(request.POST)
  • Next, we check whether the form matches the criteria of the model. Are the required fields filled out?
  • If the valid check passes, we save it. Since we’ve succeeded, we’ll want to send a fresh form back to the template, which we do by setting form = BlogForm().
  • If the request isn’t POST, that means the page is being loaded for the first time, and we just set the form = BlogForm() and render the template.

 

Step 7: Adding the Index Template

First off, we need to create the mysite/myapp/templates/myapp directory and add an index.html file. If we create the file through PyCharm, we can select the HTML file option, which will automatically generate an HTML stub for us.

  • Add myapp/templates
    • Right click on the myapp directory
    • Select New > Directory
    • Name the directory ‘templates’
  • Add another myapp, so Django knows WHICH templates to look at (very important if you have multiple apps that have a template with the same name, such as index.html)
    • Right click on the templates directory
    • Select New > Directory
    • Name the directory ‘myapp’
  • Create the index.html file
    • Right click on the new myapp directory
    • Select New > HTML File
    • Name the file ‘index’

Look, a picture:

django-settings-index-html

We need to add a few things to the HTML template to get the form to work properly. We need a form element, which contains a CSRF token (to prevent cross site request forgery), the form template variable (with the same name as the form being passed to index.html from the index view), and a submit button.

Notice we are leaving the form action=””. This means that when the form is submitted Django will call the URL of the current page (the index in this case) by matching the index URL, and the request will include a POST variable. This will pass to the index view, and the index view will process the form, then reload the index.html page! It’s a happy little circuit!

Go ahead and add the form inside the body tags of your index.html file:

 

Congratulations! You can now save form entries to the database! Fire up your local server and give it a try.

or

Like before, access your local server by typing 127.0.0.1:8000/myapp or 127.0.0.1:5000/myapp in your browser. The port number will vary depending on how you’re running the server, but the terminal should tell you the correct port to use.

Note that when saving a new entry, Django will check all of the requirements of the form (title must be unique!) and will throw an error message back if the title you entered already exists in the database. Automatic validations!

However, the resulting form is a little awkward.

django-form-demo

 

Fortunately, we can fix this by breaking out the {{form}} object in the template. Below I’ve hard-coded the field names, but you could use the Blog object’s element names instead by invoking {{form.title.label}}. Also note that in addition to the fields, we’ll need to include the {{form.id}}.

django-form-demo-2

Still not perfect, but I feel a little better!

 

Step 8: Displaying the Database Records in the Index Template

At this point you could go back to the shell and take a look at the entries you’ve added, but wouldn’t it be better if you could see your new entries instantly, right on the page? Let’s do that!

Update your view to return all entries in the Blog table by setting the ‘posts’ variable equal to the queryset result of Blog.objects.all().

We’ve created a new variable ‘posts’, which contains the queryset result of Blog.objects.all(), and added it to the response.

Next, let’s add the new queryset to the index.html template.

I won’t go into the details of the HTML, but basically, for each record in the returned ‘posts’ queryset, we are creating a row in an HTML table populated with that record’s data!

Restart your local server and refresh your page! (ctrl+c, heroku local).  NOTE: if your server won’t start up because gunicorn failed to shut down, you can use pkill gunicorn in the terminal to end the process.

 

The Finished Product!

 

django-form-demo-3

 

 

Homework? Well, Nobody’s Going to Force You. But It’s Here if You Want It!

There are some other tweaks you can make to your form such as CSS styling, default fields, tab order, or placeholders inside the text fields.

You can also add custom messages to your view using the Django messaging framework to help give the user an idea of what’s happening when they submit their form (success, error, data missing, etc).

If you get stuck, or just want to chat, please Contact Me, or leave a question/request in the comments below!

Feel free to fork or download a working version of the code from our GitHub repository if you want to use this as a jumping off point.

 

Please subscribe to our newsletter if you’d like to be notified of new postings!

(Posts) Sign Up for my Newsletter!
Sending

4 thoughts on “Implementing Django ModelForms

Join the Discussion

%d bloggers like this: