Easy Tutorial
❮ Django Cookie Session Django Orm 1 ❯

Django Forms

HTML forms are a classic way for websites to be interactive. This chapter will introduce how to process form data submitted by users using Django.


HTTP Requests

The HTTP protocol works on a "request-response" basis. When a client sends a request, it can attach data to the request. The server can obtain the data sent by the client by parsing the request and provide specific services based on the URL.

GET Method

We created a search.py file in a previous project to receive user requests:

/HelloWorld/HelloWorld/search.py File Code:

from django.http import HttpResponse
from django.shortcuts import render
# Form
def search_form(request):
    return render(request, 'search_form.html')

# Receive request data
def search(request):  
    request.encoding = 'utf-8'
    if 'q' in request.GET and request.GET['q']:
        message = 'The content you searched for: ' + request.GET['q']
    else:
        message = 'You submitted an empty form'
    return HttpResponse(message)

Add the search_form.html form in the templates directory:

/HelloWorld/templates/search_form.html File Code:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>tutorialpro.org(tutorialpro.org)</title>
</head>
<body>
    <form action="/search/" method="get">
        <input type="text" name="q">
        <input type="submit" value="Search">
    </form>
</body>
</html>

Modify the urls.py rules as follows:

/HelloWorld/HelloWorld/urls.py File Code:

from django.conf.urls import url
from . import views, testdb, search

urlpatterns = [
    url(r'^hello/$', views.tutorialpro),
    url(r'^testdb/$', testdb.testdb),
    url(r'^search-form/$', search.search_form),
    url(r'^search/$', search.search),
]

Access the address http://127.0.0.1:8000/search-form/ and search, the result is as follows:

POST Method

Above, we used the GET method, where the view display and request handling are split into two functions.

The POST method is more commonly used when submitting data. Below, we use this method with a single URL and handler function to both display the view and handle the request.

We create post.html in the templates directory:

/HelloWorld/templates/post.html File Code:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>tutorialpro.org(tutorialpro.org)</title>
</head>
<body>
    <form action="/search-post/" method="post">
        {% csrf_token %}
        <input type="text" name="q">
        <input type="submit" value="Search">
    </form>

    <p>{{ rlt }}</p>
</body>
</html>

At the end of the template, we add an rlt placeholder for the form processing result.

There is also a {% csrf_token %} tag after the form. CSRF stands for Cross Site Request Forgery. This is a feature provided by Django to prevent disguised submission requests. A form submitted with the POST method must include this tag.

Create a new search2.py file in the HelloWorld directory and use the search_post function to handle POST requests:

# -*- coding: utf-8 -*-

from django.shortcuts import render
from django.views.decorators import csrf

# Receive POST request data
def search_post(request):
    ctx ={}
    if request.POST:
        ctx['rlt'] = request.POST['q']
    return render(request, "post.html", ctx)

Modify the urls.py rules to the following form:

/HelloWorld/HelloWorld/urls.py file code:

from django.conf.urls import url
from . import views, testdb, search, search2

urlpatterns = [
    url(r'^hello/$', views.hello),
    url(r'^testdb/$', testdb.testdb),
    url(r'^search-form/$', search.search_form),
    url(r'^search/$', search.search),
    url(r'^search-post/$', search2.search_post),
]

Visiting http://127.0.0.1:8000/search-post/ displays the following result:

After completing the above example, our directory structure is:

HelloWorld
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- search.py
|   |-- search.pyc
|   |-- search2.py
|   |-- search2.pyc
|   |-- settings.py
|   |-- settings.pyc
|   |-- testdb.py
|   |-- testdb.pyc
|   |-- urls.py
|   |-- urls.pyc
|   |-- views.py
|   |-- views.pyc
|   |-- wsgi.py
|   `-- wsgi.pyc
|-- TestModel
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- admin.py
|   |-- admin.pyc
|   |-- apps.py
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   |-- 0001_initial.pyc
|   |   |-- __init__.py
|   |   `-- __init__.pyc
|   |-- models.py
|   |-- models.pyc
|   |-- tests.py
|   `-- views.py
|-- db.sqlite3
|-- manage.py
`-- templates
    |-- base.html
    |-- hello.html
    |-- post.html
    `-- search_form.html

Request Object

The first parameter of each view function is an HttpRequest object, like the tutorialpro() function below:

from django.http import HttpResponse

def tutorialpro(request):
    return HttpResponse("Hello world")

The HttpRequest object contains some information about the current request URL:

| Attribute | Description | | path | The full path to the requested page, excluding the domain name—for example, "/hello/". | | method | A string representing the HTTP method used in the request. It is all uppercase. For example: if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else() | | GET | A dictionary-like object containing all HTTP GET parameters. See the QueryDict documentation. | `` | POST | A dictionary-like object containing all HTTP POST parameters. See the QueryDict documentation. It is possible for the server to receive an empty POST request. This means that a form can submit a request via the HTTP POST method without containing any data. Therefore, you cannot use the statementif request.POSTto determine if the HTTP POST method is used; instead, useif request.method == "POST"` (see the method attribute in this table). Note: POST does not include file-upload information. See the FILES attribute. | | REQUEST | For convenience, this attribute is a combination of POST and GET attributes, with the specificity that it first looks for the POST attribute and then the GET attribute. Inspired by PHP's $_REQUEST. For example, if GET = {"name": "john"} and POST = {"age": '34'}, then REQUEST["name"] would be "john" and REQUEST["age"] would be "34". It is strongly recommended to use GET and POST as these attributes are more explicit and make the code easier to understand. | | COOKIES | A standard Python dictionary object containing all cookies. Both keys and values are strings. | | FILES | A dictionary-like object containing all uploaded files. Each key in FILES is the value of the name attribute from the <input type="file" name="" /> tag. Each value in FILES is also a standard Python dictionary object containing the following three keys: filename: the name of the uploaded file, represented as a Python string; content-type: the content type of the uploaded file; content: the raw content of the uploaded file. Note: FILES will only contain data if the request method is POST and the <form> has the attribute enctype="multipart/form-data". Otherwise, FILES will be an empty dictionary. | | META | A dictionary containing all available HTTP header information. For example: CONTENT_LENGTH; CONTENT_TYPE; QUERY_STRING: the raw, unparsed query string; REMOTE_ADDR: the client's IP address; REMOTE_HOST: the client's hostname; SERVER_NAME: the server's hostname; SERVER_PORT: the server's port. These headers, along with the prefix HTTP_, are used as keys in META, with the value following the colon (:). For example: HTTP_ACCEPT_ENCODING; HTTP_ACCEPT_LANGUAGE; HTTP_HOST: the HTTP host header sent by the client; HTTP_REFERER: the referring page; HTTP_USER_AGENT: the client's user-agent string; HTTP_X_BENDER: the X-Bender header information. | | user | A django.contrib.auth.models.User object representing the currently logged-in user. If the user is not currently logged in, user will be initialized as an instance of django.contrib.auth.models.AnonymousUser. You can distinguish whether a user is logged in by using the user's is_authenticated() method: if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users. This attribute is only available if the AuthenticationMiddleware in Django is activated. | | session | A read-write attribute representing the current session as a dictionary object. This attribute is only available if session support in Django is activated. | | raw_post_data | The raw HTTP POST data, unparsed. Useful for advanced processing. |

The Request object also has some useful methods:

Method Description
__getitem__(key) Returns the value of a key from GET/POST, prioritizing POST over GET. Throws a KeyError if the key does not exist. This allows us to access the HttpRequest object using dictionary syntax. For example, request["foo"] is equivalent to first checking request.POST["foo"] and then request.GET["foo"].
has_key() Checks if the specified key is present in request.GET or request.POST.
get_full_path() Returns the request path including the query string. For example, "/music/bands/the_beatles/?print=true"
is_secure() Returns True if the request is secure, meaning it is made via HTTPS.

QueryDict Object

In the HttpRequest object, the GET and POST attributes are instances of the django.http.QueryDict class.

QueryDict is a custom dictionary-like class designed to handle cases where a single key corresponds to multiple values.

QueryDict implements all standard dictionary methods. It also includes some unique methods:

Method Description
__getitem__ Differs slightly from the standard dictionary handling in that if the key corresponds to multiple values, __getitem__() returns the last value.
__setitem__ Sets the value list (a Python list) for the specified key. Note: It can only be called on a mutable QueryDict object (a copy of a QueryDict object created via copy()).
get() If the key corresponds to multiple values, get() returns the last value.
update() The parameter can be a QueryDict or a standard dictionary. Unlike the standard dictionary's update method, this method adds dictionary items instead of replacing them: >>> q = QueryDict('a=1')<br>>>> q = q.copy() # to make it mutable<br>>>> q.update({'a': '2'})<br>>>> q.getlist('a')<br> ['1', '2']<br>>>> q['a'] # returns the last<br>['2']
items() Differs slightly from the standard dictionary's items() method, this method uses the single-value logic of __getitem__(): >>> q = QueryDict('a=1&a=2&a=3')<br>>>> q.items()<br>[('a', '3')]
values() Differs slightly from the standard dictionary's values() method, this method uses the single-value logic of __getitem__():

Additionally, QueryDict has some methods as shown in the table below:

Method Description
copy() Returns a copy of the object, implemented using Python's standard library copy.deepcopy(). This copy is mutable, meaning its values can be changed.
getlist(key) Returns all values corresponding to the parameter key as a Python list. If the key does not exist, it returns an empty list. It is guaranteed to return a list of some sort.
setlist(key, list_) Sets the value of key to list_ (unlike __setitem__()).
appendlist(key, item) Appends item to the internal list associated with key.
setlistdefault(key, list) Differs slightly from setdefault, it accepts a list instead of a single value as a parameter.
lists() Differs slightly from items(), it returns all values for the key as a list, for example: >>> q = QueryDict('a=1&a=2&a=3')<br>>>> q.lists()<br>[('a', ['1', '2', '3'])]
urlencode() Returns a string formatted as a query string (e.g., "a=2&b=3&b=5").
❮ Django Cookie Session Django Orm 1 ❯