In this tutorial, we’ll be discussing Forms in our Django Project. In the previous tutorials, we had discussed Django Templates and Django Models. Today, we’ll see how Django Forms work and use them with Templates as well.
Django Forms
Forms are an essential part of any web application that requires user input. Be it login forms, entering survey details, writing blog posts and comments(like we do on JournalDev!).
Django Forms basically does three simple things:
- Read user input
- Validate it.
- Convert it into Python data types/objects
Models map fields into types for the database. Forms map fields into Python types.
Let’s first discuss HTML forms since those are what would be finally displayed on the screen.
HTML Forms
To create a form in HTML, enclose it in the <form> ... <form/>
tags
Example:
<form action="/goto_url/" method="post">
<label for="name">Enter name: </label>
<input id="name" type="text" name="name_field" value=".">
<input type="submit" value="OK">
</form>
The form tag consists of an action which takes you to the mentioned url
path when submit
input type is clicked.
In the method, we set it to GET or POST normally. The label acts as a hint for the id of the input tag it is linked to.
Note: There are several other fields such as DateField, BooleanField and many more that can be used inside forms.
GET is used to send the data in the form of a string which gets appended to the URL. This doesn’t change anything in the database.
POST method is used to bundle up the data and send it to the server. It gets a response back. This is normally used to update the database.
GET is vulnerable to cross forgery site attacks since the data is available in the url itself.
GET shouldn’t be used in cases such as password forms. A POST is more resistant to attacks.
Django Form class
Django makes our lives easier by handling the tiny details such as creating and re-creating the forms in the HTML page, validating the data entered and performing any actions set upon the forms.
Just like HTML has the form tag, Django has a Form
class.
The Form class is defined as:
from django import forms
class FirstForm(forms.Form):
name = forms.CharField(label='Your name', max_length=100)
We can render this class using Django Templates in the HTML page.
When the Submit button is clicked, the Form class would do the validation check using the is_valid() method on the instance of the above class.
Once the validation is cleared the form data is available in the Form class’s cleaned_data
attribute.
Django Forms can be of two types:
unbound
– No data is present in the forms. They are empty.
bound
– Data is filled in these types of forms.
The above concepts may be difficult to digest. The best possible way is to learn by example.
In the next section, we’ll create a basic Django Web Application with Forms implemented.
Our application would take responses and show it on the next screen.
Quick Setup
Let’s create a new Django Project and start a new app inside it named responseapp
.
Following is the ordered list of commands we’d entered in the terminal. To know the details visit our First Django Tutorial.
mkdir DjangoForms
cd DjangoForms
virtualenv -p /usr/local/bin/python3 env
source env/bin/activate
pip3 install django
django-admin startproject DjangoFormsBasics
cd DjangoFormsBasics
python3 manage.py runserver
django-admin startapp responseapp
cd responseapp
mkdir templates
cd templates
touch responseform.html
touch thankyou.html
Inside the responseapp
, we’ve created a templates folder which will hold the html files.
Inside the templates folder, add two html files for the two paged web application we’ll build next.
Create two new python files: forms.py
and urls.py
:
cd ..
touch forms.py
touch urls.py
Project Structure
Don’t forget to add the Django app in the settings.py
file:
Code
Add the following code in your forms.py file:
from django import forms
class MyForm(forms.Form):
name = forms.CharField(label='Enter your name', max_length=100)
email = forms.EmailField(label='Enter your email', max_length=100)
feedback = forms.CharField(widget=forms.Textarea(attrs={'width':"100%", 'cols' : "80", 'rows': "20", }))
We’ve added three fields: CharFields, EmailFields, and a CharField with TextArea width and height specified.
The code for views.py file is given below:
from django.shortcuts import render
from responseapp.forms import MyForm
def responseform(request):
form = MyForm()
return render(request, 'responseform.html', {'form':form});
You must use csrf(Cross Site Request Forgeries) for Django Forms which have the method POST.
This renders the Django Form and uses the template language by passing the complete form instance to the HTML.
The code for our initial responseform.html class is given below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Forms Tutorial</title>
</head>
<body>
<h2>Django Forms Tutorial</h2>
<form>
{% csrf_token %}
{{form}}
<input type="submit" value="Submit" />
</form>
</body>
</html>
Run the following commands on your terminal on the inner DjangoFormBasics folder:
python3 manage.py migrate
python3 manage.py runserver
Note: You must specify the url patterns. Check out the urls.py files defined later in this tutorial.
Following is the output of the application in action.
WHOOPS! It looks ugly horizontally. We can arrange the Forms in the following orders:
form.as_ul
: Display fields as unordered listform.as_p
: Display fields as paragraph in separate lineform.as_table
: Display fields as table elements
<table>
{{form.as_table}}
</table>
For form_as_ul
you must enclose it in the ul
tag.
Also, the submit button doesn’t work, let’s add another html page which will catch the form responses and display there.
The updated code for the responseform.html is given below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Forms Tutorial</title>
</head>
<body>
<h2>Django Forms Tutorial</h2>
<form action="/thankyou/" method="post">
{% csrf_token %}
<table>
{{form.as_table}}
</table>
<input type="submit" value="Submit" />
</form>
</body>
</html>
In the above code, we’ve added a method and action. When submit is clicked the user would be taken to the /thankyou/ page with the form data POSTed.
The code for the views.py file is given below:
from django.shortcuts import render
from responseapp.forms import MyForm
from django.template import loader
from django.http import HttpResponse
def responseform(request):
#if form is submitted
if request.method == 'POST':
myForm = MyForm(request.POST)
if myForm.is_valid():
name = myForm.cleaned_data['name']
email = myForm.cleaned_data['email']
feedback = myForm.cleaned_data['feedback']
context = {
'name': name,
'email': email,
'feedback': feedback
}
template = loader.get_template('thankyou.html')
return HttpResponse(template.render(context, request))
else:
form = MyForm()
return render(request, 'responseform.html', {'form':form});
Initially, the else
statement will execute and create an empty form.
Later when submit is clicked, if
block is executed and if the form is validated we load the thankyou.html page using Loaders with Django Templates.
The form data is passed to the thankyou.html
class as:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Thank You</title>
</head>
<body>
<h2>Response Entered by you:</h2>
<form method="post">
<ul>
<li>Name: <strong>{{ name }}</strong></li>
<li>Email: <strong>{{ email }}</strong></li>
<li>Feedback: <strong>{{ feedback }}</strong></li>
</ul>
</form>
</body>
</html>
The code for the urls.py
created inside the responseapp
folder is:
from django.urls import path
from django.contrib import admin
from responseapp import views as responseapp_views
urlpatterns = [
path('response/', responseapp_views.responseform),
path('thankyou/', responseapp_views.responseform),
path('', admin.site.urls),
]
responseapp_views
is same as responseapp.views
. It calls the views.py file from where the application starts.
The below urls.py
file must be included in the outer urls.py
file defined in the DjangoForm project:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('responseapp.urls'))
]
The output of the application in action is:
This brings an end to this tutorial. You can download the project from below:
after some debugging this program form works fine thanks
please help me with my following error.
Request Method: POST
Request URL: http://localhost:7000/response/thankyou/
Django Version: 3.1.5
Exception Type: TypeError
Exception Value:
list indices must be integers or slices, not str
the error is in the following line>>>
if form.is_valid():
name = form.changed_data[‘name’]
email = form.changed_data[’email’]
feedback = form.changed_data[‘feedback’]
Blog was helpful. Thanks
i have same problem
it does not work
python3 manage.py migrate
module ‘django.contrib’ has no attribute ‘staticfilesresponseapp’
Thanks for the awesome article, you just made my day. I need one help with the form save in sql.
How do I do that? Please help me how to save the form and view in separate template