By Md. Sabuj Sarker | 8/22/2017 | General |Beginners

Working with forms in Django

Working with forms in Django

A web application without forms looks incomplete. In real life you would rarely find a web application without forms unless that is only an API endpoint. But when forms come into play there come cyber security issues too. Again, in an Object Oriented Programming environment we need everything as objects. An HTML form does not go with OOP well but we can make it work. Form validation is also a very fundamental demand of every web application. All the problems related to forms can be solved with Django forms.

Plain old HTML form with Django

Let's first work with plain old HTML forms with Django. We need to create a view that will return an HTML page containing an HTML form. We will post to the same view for the data to be processed. As we need both post and get methods we can use class based views instead of function based ones. Our view will use a template to display the form and when the data is submitted it will return the data back to the page.

We are going to work with a person's data. The form will contain the person's name, age and height. We worked with similar data in the ORM lesson. We created a Person model class then. In this article we will learn to save data with that model.

Create a template file inside app1/templates/app1 named my_form.html

<!DOCTYPE html>
<html>
<body>
   <form method="POST" action="/my-form">
       Name:<br>
       <input type="text" name="name">
       <br>
       Age:<br>
       <input type="text" name="age">
       <br>
       Height: <br>
       <input type="text" name="height">
       <br><br>
       <input type="submit" value="Submit">
   </form> 
</body>
</html>

Update the urlpatterns

from django.conf.urls import url
from app1.views import PersonView
urlpatterns = [
   url(r'^my-form$', PersonView.as_view())
]

Create the view in views.py

from django.shortcuts import render
from django.views import View

class PersonView(View):
   def get(self, request):
       return render(request, "app1/my_form.html")

   def post(self, request):
       pass

We will complete the post() method later. Let's test our work.

CSRF protection

Visit the URL with your browser: http://localhost:8000/my-form to see the form. Now, fill the form with some data and hit submit. You will see a result like the following.

Forbidden (403)
CSRF verification failed. Request aborted.
Help
Reason given for failure:
   CSRF token missing or incorrect.

...
...
...

It's a forbidden 403 forbidden error. By default we have Cross Site Request Forgery (CSRF) protection turned on. We can turn this off in our setings.py file but we should not do that. Instead, we should provide CSRF token with our form. There is a template tag to provide a CSRF token with a form that is {% csrf_token %}. Our template becomes:

<!DOCTYPE html>
<html>
<body>
   <form method="POST" action="/my-form">
       {% csrf_token %}
       Name:<br>
       <input type="text" name="name">
       <br>
       Age:<br>
       <input type="text" name="age">
       <br>
       Height: <br>
       <input type="text" name="height">
       <br><br>
       <input type="submit" value="Submit">
   </form> 
</body>
</html>

Visit the URL again, fill in the form and hit submit to see a result like the following

ValueError at /my-form
The view app1.views.PersonView didn't return an HttpResponse object. It returned None instead.
...
...
...

The error is no surprise because the form submission is hitting the post() method and our post() method is not properly defined yet. The form data submitted with the POST method is stored in the HttpRequest object's POST attribute that is a dictionary like object. Let's grab the data and return the data to the browser as formatted HTML.

Grabbing form data

from django.shortcuts import render
from django.views import View

class PersonView(View):
   def get(self, request):
       return render(request, "app1/my_form.html")

   def post(self, request):
       name = request.POST['name']
       age = request.POST['age']
       height = request.POST['height']

       context = {
           'person': {
               'name': name,
               'age': age,
               'height': height
           }
       }

       return render(request, "app1/my_form.html", context=context)

To display the data with the same form let's use the same template with some modification.

<!DOCTYPE html>
<html>
<body>
   {% if person %}
   Submitted person data: <br>
       Name: {{ person.name }} <br>
       Age: {{ person.age }} <br>
       Height: {{ person.height }} <br>
   {% endif %}
   <hr>
   <br>
   <form method="POST" action="/my-form">
       {% csrf_token %}
       Name:<br>
       <input type="text" name="name">
       <br>
       Age:<br>
       <input type="text" name="age">
       <br>
       Height: <br>
       <input type="text" name="height">
       <br><br>
       <input type="submit" value="Submit">
   </form> 
</body>
</html>

Visit the url again, fill up the form and hit submit. Now you will be shown the filled up data along with the form.

Saving form data to the database with Django ORM

In a previous article we learned how to work with Django ORM. Let's use our previous knowledge to save data to the database. We need to import our model first.

from app1.models import Person
...
...
...

Note: we get data as string from form. We need to convert them to appropriate python type to be used with Django Models.

from django.shortcuts import render
from django.views import View
from app1.models import Person

class PersonView(View):
   def get(self, request):
       return render(request, "app1/my_form.html")

   def post(self, request):
       name = request.POST['name']
       age = request.POST['age']
       height = request.POST['height']
       age = int(age)
       height = int(height)

       p = Person()
       p.name = name
       p.age = age
       p.height = height
       p.save()

       context = {
           'person': {
               'name': name,
               'age': age,
               'height': height
           }
       }

       return render(request, "app1/my_form.html", context=context)

Our data will be persisted in the database. You may verify the result by querying your database.

Using Django Forms

We want a more modular way, a more Object Oriented way, more Pythonic way, more Django-ish style of using forms with Django. Django is not going to disappoint you. Django provides a better way to work with forms and their validation. Django provides a way to work with forms that is similar to Django ORM.

Create a file named forms.py inside the app1 folder.

from django import forms

class PersonForm(forms.Form):
   name = forms.CharField(required=True, label="Name", max_length=64)
   age = forms.IntegerField(required=True, label="Age")
   height = forms.FloatField(required=True, label="Height")

Look, we have fields like Django database models. The fields are of various types and with some rules like how long a name can be, whether a field could be optional or not, etc. Look at the official reference manual to know about various form field types.

Update the view

from django.shortcuts import render
from django.views import View
from app1.models import Person
from app1.forms import PersonForm

class PersonView(View):
   def get(self, request):
       pd = PersonForm()
       return render(request, "app1/my_form.html", context={ 'form': pd })

   def post(self, request):
       pd = PersonForm(request.POST)
       if pd.is_valid():
           name = pd.cleaned_data['name']
           age = pd.cleaned_data['age']
           height = pd.cleaned_data['height']

           p = Person()
           p.name = name
           p.age = age
           p.height = height
           p.save()

           context = {
               'person': {
                   'name': name,
                   'age': age,
                   'height': height,
               },
               'form': pd
           }
       else:
           print("Form is invalid")
           context = { 'form': pd }

       return render(request, "app1/my_form.html", context=context)

We populate the form with request.POST data. We check if form is valid according to the rules we have set by using the is_valid() method. If the form is valid we save it otherwise we don't. When the form is invalid we print that info on standard output. If we want we can also show errors through HTML but for now we are saving that for a future article. We also added a context variable called form so that we can access the form data from the template.

If we do not want to write pure HTML to code the form we can do it in the following way.

<!DOCTYPE html>
<html>
<body>
   {% if person %}
   Submitted person data: <br>
       Name: {{ person.name }} <br>
       Age: {{ person.age }} <br>
       Height: {{ person.height }} <br>
   {% endif %}
   <hr>
   <br>
   <form method="POST" action="/my-form">
       {% csrf_token %}
       {{ form.as_p }}
       <br><br>
       <input type="submit" value="Submit">
   </form> 
</body>
</html>

Visit the url again. Fill the form, hit the submit button and you will see your desired result with data saved to the database. Also notice that your form is pre-populated with the data you submitted. It is convenient to have data pre-populated if we want to start working from where we left off.

That's all for today. We would continue to work on forms, ORM models, templates and other things in future articles and once we cover more basics we will go for advanced usage of what we have learnt so far. Happy programming with Python and Django.

By Md. Sabuj Sarker | 8/22/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now