Displaying Forms in Django

So far we have been using Django Shell to demonstrate how the forms work. In this lesson, we will learn how to display forms in our templates.

Form Display Methods

Django provides the following three methods to display form elements:

  1. as_p()
  2. as_table()
  3. as_ul()

as_p()

This method displays form fields a series of <p> tags:

as_table()

This method displays form fields a series of <tr> tags:

as_ul()

This method displays form fields a series of <li> tags:

Notice that the rendered HTML doesn’t have the <form> tag and the submit button. The form methods only output the form fields. To make forms fully functional, we have to manually add <form> tag and submit button like this:

We can also output form fields in the template by just typing {{ f }}, which is equivalent to {{ f.as_table }}.

In bound state of the form, these methods also output validation errors along with the data filled in the previous request.

Let’s bind the form f2 with some data.

We have explored the forms enough in the shell, let’s now create a real form.

Creating a Real Form

Open views.py file from djangobin app and add add_lang() view towards the end of the file:

djangobin/django_project/djangobin/views.py

Here is how it works:

  1. When a GET request comes, we create an unbound LanguageForm (line 18) and render an empty form (line 20).
  2. If the request is POST (line 11), we create a bound form (line 12) and validate it using is_valid() method. If the form is valid, we save the language and redirect the user to the add language page. On the other hand, if the validation fails, control comes out of the if-else statement and we return a new response containing form data as well as the validation errors (line 20).

Next, open urls.py and add add_lang URL patterns at the end of the file:

djangobin/django_project/djangobin/urls.py

Finally, here is the code for add_lang.html template.

djangobin/django_project/djangobin/templates/djangobin/add_lang.html

Nothing new here, except the {% csrf_token %} tag. The csrf_token is a special tag which Django uses to prevent CSRF (Cross-Site Request Forgery) attacks. You don’t need to know how it works internally, just put {% csrf_token %} in your form templates and Django will take care of everything else. If you are interested in learning more about CSRF attacks click here.

By default, Django expects you to add csrf_token tag on every form. If you don’t do so, then on submitting the form you would get an HTTP 403 FORBIDDEN error like this:

Start the server using ./manage.py runserver and visit http://localhost:8000/add-lang/. You will see an Add language like this:

Enter some duplicate data in the fields with a unique constraint. You will get validation errors like this:

The validation error for a field is displayed above the field itself. Also, notice the error at the top of the form which says “Slug and MIME shouldn’t be same.”. This is a non-field error, as a result, it is displayed at the top of the form above all the fields.

Keep in mind that LanguageForm form is performing two validations: form validation and model validation. The non-field error (“Slug and MIME shouldn’t be same”) is coming from form validation whereas errors about duplicate data are coming from model validation.

Now, enter a new language which doesn’t already exists in the database. This time, the form will save the data into the database and then you will be redirected to the Add language page.

Our data is saved but we didn’t get any confirmation about it. We can easily fix this issue using flash messages.

Flash Message

A web application is all about user experience. After every action, you must notify the user about the result of the operation. These notifications are also commonly known as Flash Messages. Django provides a built-in framework named django.contrib.messages to display flash messages. The django.contrib.messages framework already comes preinstalled so you don’t have to configure anything to use it.

To display flash messages we have to first import messages package from the django.contrib package.

The messages package provides a function named add_message() to set flash messages. The add_message() function accepts two arguments, the request object and the message you want to display. Here is an example:

We can also pass message levels to the add_message() function. The message levels allow us to format the flash message in the template. The following table lists built-in messages levels, which can be imported from django.contrib.messages package.

Constant Description
DEBUG It is used to display development related messages.
INFO It is used to display informational messages.
SUCCESS It is used to display success related messages.
WARNING It is used to display warning related messages.
ERROR It is used to display error related messages.

Here are some examples of how to set message levels:

To access flash messages in the templates, we use the messages variable as follows:

Just like request variable, the messages is another one of those special variables which you always have access to inside the template if you use the render() function.

Now you know how to use flash messages let’s put it to use in Add language form.

Open djangobin’s views.py and modify to use django.contrib.messages framework as follows:

djangobin/django_project/djangobin/views.py

Next, to display the flash message modify add_lang.html template as follows:

Revisit add post form at http://127.0.0.1:8000/cadmin/post/add/ and add a language. This time you will get a success message like this:

Updating Data via Forms

The forms we have built until now, can only create new records? What about updating those records?

In updating a record the first step is to show a form pre-populated with data from the database. Django provides an instance parameter just for this task. The following shell session demonstrates how to use it.

Notice that the output of print(f.as_p()) contains all the form fields pre-populated with data from the database.

At this point, you might think, “Our form has data so it should be in bound state” right? The answer is: No, the form is still in the unbound state. The use of instance attribute is only restricted to displaying data. That’s it. We can verify this fact by using is_bound attribute on the form object.

So how we bind data to the form while updating objects?

To bind the data to the form pass a dictionary containing data, along with the instance attribute like this:

Although the dictionary is empty, our form is still in the bound state.

In the real world, we would pass request.POST instead of an empty dictionary ({}).

Another important thing to keep in mind is that while saving the data, the save() method will use data from request.POST not from instance=c.

Let’s use this knowledge to create Change language form.

Open djangobin’s views.py and add the update_lang() view function below the add_lang() view as follows:

djangobin/django_project/djangobin/views.py

In urls.py file, add update_lang URL pattern as follows:

djangobin/django_project/djangobin/urls.py

And here is the code for update_lang.html template:

djangobin/django_project/djangobin/templates/djangobin/update_lang.html

Visit http://localhost:8000/update-lang/sql/ and you will see Change language page like this:

Just like Add language page, Change language page shows validation errors as well as pre-populates the data from the previous request. When you are done updating the language, hit “Submit” button to save your changes to the database.

1 thought on “Displaying Forms in Django

Leave a Comment