This article is part 11 of 14 in the series Python Django Tutorial

Last Updated: Saturday 8th February 2014

Django Form

What is a HTML form? What kind of use cases does it have?

A webform, web form or HTML form on a web page allows a user to enter data that is sent to a server for processing. Forms can resemble paper or database forms because web users fill out the forms using checkboxes, radio buttons, or text fields. For example, forms can be used to enter shipping or credit card data to order a product, or can be used to retrieve search results from a search engine.

A HTML form is the most common way to send data from a client application to a server. In this article, we are going to learn how to use Django's Form library to handle HTML forms.

Benefits of Using Django's Form Library

In one of the previous articles, Writing Views to Upload Posts for Your First Python Django Application, we learned how to use request.POST variable to retrieve data from a HTML form. However, simply retrieving data is not enough because we would probably want to handle data validation and avoid writing the HTML form manually in a template.

Luckily, Django's form library is designed to provide the following benefits:

  • Auto-generate a HTML form from a form widget.
  • Validate submitted data against a set of rules.
  • Redisplay the HTML form with errors if there's any.
  • Convert submitted data into Python compatible types.

Basic Concepts

Before we dive into the details about writing a form, we need to know the following concepts:

  • Widget: A Python class that renders itself into an HTML form.
  • Field: A Python class that validates the data, e.g. a CharField that limits the number of maximum characters.
  • Form: A collection of fields.
  • Form Assets: CSS and Javascript associated with rendering a form.

Designed correctly, the Form library is decoupled from other Django components, such as the database layer and even templates. By separating the Form library from the rest of Django, we can write and test forms independently from the rest of our application, which increases the maintainability of our application.

Forms and Views

Now let's go ahead and create a form object that represents a Post in myblog/forms.py.

Then we create a new view that returns our form in the context.

Then we create a new template that renders our form inside an HTML page in myblog/templates/post_form_upload.html.

Finally, we modify myblog/urls.py to append the following entry:

Now we can access the URL http://localhost:8000/post/form_upload.html to take a look at our new post form page:

Our empty form page

Our empty form page.

Filling out the form and clicking "submit" redirect you to the new post's detail page:

Django Form Screenshot 2

Our form filled in.

Django Form Posted Screenshot

Our page after being submitted.

Since both the "content" and "created at" fields are required, you will get an error when you try to upload an empty field.

Required input error.

Required input error..

Summary and Tips

In this article, we learned how to use Django's form library to handle HTML forms. While it's common practice to create an HTML form by hand, creating Django's form objects provides better maintainability, readability and functionality. In the next article, we are going to learn how to use Django's ModelForm to auto-generate forms from models.

About The Author

  • Empire

    I think it wold help if you explained why you used ‘post_form_upload’ AND ‘form_upload’.
    Overall, great. Thanks.

    • Xiaonuo Gantan

      ‘post_form_upload’ is the name of the post view function and ‘post/form_upload.html’ is the relative URL of the form post view specified in urls.py. These two are normal Python function names and strings. There is nothing special why I used these two instead of others. You can choose whatever you like for the view function and the relative URL, as long as it makes sense to you and easy for others to understand their purposes.

  • Barak

    Thank you for this! Great explanation.

  • Ishwar Chincholkar

    TemplateDoesNotExist at /post_form_upload/

    it give me error when i run above code

    • Xiaonuo Gantan

      Before running the code, make sure you have created a template file at “myblog/templates/post_form_upload.html” with the code shown in this article. The directory “myblog/” also contains other top-level project files such as views.py and urls.py.

  • Ishwar Chincholkar

    Xiaonuo Gantan Thank you…

  • Ishwar Chincholkar

    I want to implement tree structure view for file exploration…
    Please give me a example how to code it… In django

  • Eddwin Paz

    How about editing. How do people do for editing the same form to update data… The tutorial is good but lacks of CRUD operations don’t you think??

  • Dat

    Well, it took me forever to find an article that actually tells me where to put my form’s template. Putting my template html file in my app’s template’s directory, as instructed for models in the official documentation, does not work! Reading this tutorial lead me to put the template in my project’s root template directory, which DOES work. So, thank you! …The official documentation is too wordy…

  • satish

    global name ‘m’ is not defined exception is coming

    • Xiaonuo Gantan

      ‘m’ is the models module defined in ‘myblog/models.py’ which was mentioned in the previous article. You can import it as ‘from myblog import models as m’.

  • sreehari

    why I am getting this error – Reverse for ‘post_detail’ with arguments ‘()’ and keyword arguments ‘{‘post_id': 9L}’ not found. 0 pattern(s) tried: []

    • sreehari

      thanks Gantan .I solved the issue…

  • Chris Clifford

    I am getting this error:

    Reverse for ‘post_detail’ with arguments ‘()’ and keyword arguments ‘{‘post_id': 6}’ not found.

    I do not know how to work around it.

    • Chris Clifford

      I fixed it. The problem is that we switched to using the PostDetailView in a previous article, but the post_upload and then post_form_upload (from this article) views were written as if we were still using the old non-generic view. The fix is to change the final line in both views to:

      return HttpResponseRedirect(reverse(‘post_detail’, kwargs={‘pk': post.id}))

  • Adriann Felipe Sanchez Sierra

    Can you gift me the code??

  • don

    Very much a beginner here. I would love to see what the details.html looks like.