Introduction
In this chapter, we will learn to use Django Forms.
Django Forms
Forms are something that we have to deal with on a daily basis but yet they can be boring as they have a lot of fields to be filled in. Forms power everything from Google's search box to Facebook's Like button. Unless you’re planning to build websites and applications that do nothing but publish content and don’t accept input from users, you are going to need forms.
Django abstracts most of the grunt work while working with forms such as validation or presentation. It also implements various security best practices. For the sake of this chapter, we’ll have a brief introduction to the following:
- Overview of the form library in Django
- Understanding the Rendering process
- CSRF token for Untrusted input
- Customizing error messages
- Widgets
Now let’s take a look inside Django forms.
Django forms library
Django comes with its own library called forms to handle the states and tasks associated with forms. The Django forms library handles three common tasks:
- HTML form generation.
- Server-side validation of user input
- HTML form redisplays in case of input errors
This library works in a similar fashion to Django’s model. Let’s create a simple signup form and see how things work.
To work with forms, create a new Python file named forms.py in your app and add the following code to it.
- from django import forms
- from django.contrib.auth.models import User
- class UserForm(forms.ModelForm):
- password = forms.CharField(widget=forms.PasswordInput)
- class Meta:
- model = User
- fields = ['username', 'email', 'password']
We imported the forms class and User model from Django. The beauty of Django lies in its ease. Did you notice that we didn’t have to create the User model? Django already has it created for us. It comes with all the general fields required for a user like first_name, last_name, email, password, user_permissions, etc. If you have any confusion regarding creating and manipulating models then please refer to the Django Models chapter.
Next, we defined our UserForm class which is inheriting from form class. There could be two ways to inherit from form class.
forms.ModelForm
If our form is going to be used to directly add or edit a Django Model then you can use ModelForm. It is used to avoid the duplication of the model description as we did in the above case.
forms.Form
Forms created from forms. The form is manually configured by you. It is better to use this way of creating forms that do not directly interact with models. For example, a contact form or newsletter subscriptions form where database interaction is not required. One such example could be,
- from django import forms
- class ContactForm(forms.Form):
- subject = forms.CharField(max_length=100)
- message = forms.CharField()
- sender = forms.EmailField()
Now let’s get back where we left our forms.py code explanation. We defined the password field as CharField and used the PasswordInput widget for this.
We also defined the Meta class for the UserForm class. Think of the Meta class as a container for configuration attributes of the outer class. The attributes of a class (for those that inherit from Model) are expected to be fields that correspond to their counterparts in the database. As new instances of your outer class are created, the class constructor will look to the Meta attribute for specific configuration details.
Now let’s update our views. Open views.py and edit the following code to it.
- from django.shortcuts import render, redirect
- from django.contrib.auth import login, authenticate
- from django.views.generic import View
- from webapp.forms import UserForm
-
- class UserFormView(View):
- form_class = UserForm
- template_name = 'registration/signup.html'
-
-
- def get(self, request):
- form = self.form_class(None)
- return render(request, self.template_name, {'form': form})
-
-
- def post(self, request):
- form = self.form_class(request.POST)
-
- if form.is_valid():
-
- user = form.save(commit=False)
-
-
-
- username = form.cleaned_data['username']
- password = form.cleaned_data['password']
- user.set_password(password)
- user.save()
-
-
-
- user = authenticate(username=username, password=password)
-
- if user is not None:
- if user.is_active:
- login(request, user)
- return redirect('home')
- return render(request, self.template_name, {'form': form})
Let’s try to understand the code here.
We did a bunch of imports from Django. These imports should be familiar to you as we’ve already talked about them already.
A view is a callable that takes a request and returns a response. A powerful way of using these generic views is to inherit from an existing view and override attributes (such as the template_name) or methods in a subclass to provide new values or methods which is pretty much what we did here.
We then defined the UserFormView class specifying the UserForm as parent class and template name. template_name refers to the template designated for signup which we’ll be creating next. For the moment; just link the template here.
There are two HTTP methods that are used to deal with forms; GET and POST. Any request that could be used to change the state of the system - for example, a request that makes changes in the database - should use POST. GET should be used only for requests that do not affect the state of the system. So we defined two different methods depending on the method chosen. If the GET method is used then the user will be displayed a blank form. If the POST method is used then the user will be able to register.
There are a few more things that we haven’t talked about like form.is_valid(), form.cleaned_data, and user.is_active. Let’s take a moment and see what these are.
form.is_valid()
Our registration process is kind of dependent on form.is_valid() function. The form goes through several states during its request-response cycle. In the simplest scenario, you need to present an empty form, and the user fills it incorrectly and submits it.
In other cases, they enter some invalid data and the form needs to be resubmitted until the entire form is valid.
This could be summarized as:
- Unbound form
a blank form is called an unbound form in Django.
- Bound form
the form that has filled fields is called a bound form in Django
- Invalid form
form submitted with errors would be called bound but invalid form
- Valid
submitted form without any error is called a bound and valid form
If is_valid() returns true, then every field in the bound form has valid data. If false, then there was some invalid data in at least one field or the form was not bound. This check is necessary as we don’t want the user to insert invalid data.
form.cleaned_data
We need to get the ‘cleaned data’ from the form. What? Does this mean the values that the user has entered were not clean?
The answer is Yes. Following are a few reasons for that,
- First
You cannot trust users from the outside world with anything. There could be hackers sitting out there who can enter all sorts of exploits through a form to compromise the security of your site. So, any form of data must be sanitized before you use them. Think of it as how you have to wash your hands before a meal.
- Secondly
The field values are just strings. Although Django offers different field types like an integer or date or whatever, these would be sent as strings to your view. Surely, you would want to convert them to the appropriate Python types before use. The form class will do the conversion automatically for you while cleaning.
Last but not least, we check the status of the user just to make sure than he is active and the account is not being suspended by the admin. If user.is_active is TRUE and entered credentials were correct then we return a user object.
Now let’s create our template to display the signup form. Create a directory ‘registration’ inside your templates directory. Now create signup.html file inside ‘registration’ folder and add the following code to it.
- {% extends 'index.html' %}
- {% block content %}
- <h2>Sign up</h2>
- <form method="post">
- {% csrf_token %}
- {{ form.as_p }}
- <button type="submit">Sign up</button>
- </form>
- {% endblock %}
What we did is defined our form using form tag same as HTML and specifying the method ‘post’.
CSRF_Token
It is the security mechanism against Cross-Site Request Forgery (CSRF) attacks for your forms This type of attack occurs when a malicious web site contains a link, a form button or some javascript that is intended to perform some action on your web site, using the credentials of a logged-in user who visits the malicious site in their browser. This token is used as a security measure against ensuring that the form was generated for the user by the original site and not by an attacker with similar fields. Django won’t accept form creation without CSRF token.
Lastly, we rendered the form using {{form.as_p}}. We’ll get back to the rendering process in a while.
Now the last thing before user registration becomes live; URL. Edit urls.py file as follow,
- from django.contrib import admin
- from django.conf.urls import url
- from webapp.views import *
- urlpatterns = [
- url('admin/', admin.site.urls),
- url(r'^$', main_page, name='home'),
- url(r'^signup/$', UserFormView.as_view()),
- ]
Now start your web server and navigate to http://127.0.0.1:8000/signup/ and you should see the sign-up page that you created.
Understanding the Rendering Process
Take a close look at the form template that we just used. Don’t you think it works like magic? By that I mean the form itself may contain tens of fields but we use a simple {{form}} tag to render it all in the template. One more thing we used {{form.as_p}} tag. What this .as_p is?
Django supports three different representations for HTML form.
- as_p()
- as_ul()
- as_table()
as_p() would render the above form like this.
Figure 1: rendering form using {{form.as_p}}
You can try rendering the above form using the other two representations. The output for the other two representations would look something like this.
Figure 2 rendering form using {{form.as_ul}}
Figure 3: rendering form using {{form.as_table}}
Let’s try something. Try submitting an empty form. What happened? You won’t be able to submit the form as Django will prompt you that the fields are required. The same would happen if you try to register with a username that already exists or an invalid email address. But we didn’t implement any kind of validation. Remember that is_valid()? All this validation is working because of that. In other words, Django did everything for us.
Customizing Error message
Displaying a user-specific error message can be important. For example, you would have come across different sites that have a special demand for the password as it should contain at least one number, a special character, an upper case, etc. Suppose a user chooses a password but it doesn't contain a special character. Now it’s important to tell the user what is missing. Django provides us a way to customize the error messages using the error_messages property in the form field and set any string as an error message. It is also possible to define different error messages depending upon the type of error. Say we want to display a different error message if the form field is empty or if the format is not correct e.g. user set the only numeric password. We can do this by defining a dictionary as follows,
- class UserForm(forms.ModelForm):
- password = forms.CharField(widget=forms.PasswordInput, error_messages={
- 'required': 'You must choose a password!'
- })
This is a common practice to display customized error messages as it improves the quality of the website.
Widgets
A widget is Django’s representation of an HTML input element. It is an effective way to customize the display of the form element. Whenever we specify a field on a form, Django will use a default widget that is appropriate to the type of data that is to be displayed. Suppose we want to create a contact form. It could be done in the following manner:
- class ContactForm(forms.Form):
-
- name = forms.CharField()
- email = forms.EmailField()
- message = forms.CharField(widget=forms.Textarea)
This contact form will display a Textarea for the message. That’s not it; you can also specify the dimensions for Textarea if you want. Like this:
- message = forms.CharField(widget=forms.Textarea(attrs={'rows': 5, 'cols': 50}))
Let’s see another example. We want to create a form that also lets the user choose their gender. This option can be provided as follows:
- class Form(forms.Form):
-
- Gender_Choices = ('Male', 'Female')
- Gender = forms.ChoiceField(widget=forms.RadioSelect, choices=Gender_Choices)
Summary
In the next chapter, we will learn to perform CRUD operations in Django,