English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Django Quick Start-forms

In the previous section of the tutorial, we introduced Django views, and wrote a simple example. In this section, we will learn about web voting applications and focus on simple form processing with the least amount of code.

Write a simple form

Let's update the poll detail template ('polls/detail.html), from the previous tutorial, in the template polls/templates/polls/detail.html contains an HTML<form>element:

# Filename : example.py
# Copyright : 2020 By w3codebox
# Author by : www.oldtoolbag.com
# Date : 2020-08-08
<h1{{{ question.question_text }}}</h1>
 {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
 <form action="{% url 'polls:vote' question.id %}" method="post">
 {% csrf_token %}
 {% for choice in question.choice_set.all %}
     <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
     <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label>/label><br />
 {% endfor %}
 <input type="submit" value="Vote" />
 </The template above shows each question with a single choice radio button. The value of each radio button is associated with the question's choice number. The name of each radio button is 'choice'. This means that when someone selects one of the radio buttons and submits the form, it will send POST data choice=#, where # is the ID of the selected choice. This is a basic concept of HTML forms.  
  We set the form's action to {% url 'polls:vote' question.id %}, and set method="post". It is very important to use method="post" (in contrast to method="get") because submitting this form will change the behavior of the server-side data. When creating a form that changes server-side data, use method="post". This article is not just for Django; it is a good Web development practice.  
  forloop.counter indicates how many times the form tag has been cycled through  
  Since we are creating a POST form (which can affect data), we need to be concerned about Cross-Site Request Forgery (CSRF). But don't worry, Django comes with a very easy-to-use system to protect against it. In summary, for all POST forms targeting internal URLs, it is recommended to use the {%csrf_token%} template tag.

We have created a virtual implementation of the vote() function. Now let's create a practical version. Add the following code to the polls file/views.py:

polls/The content of the views.py file is as follows:

# Filename : example.py
# Copyright : 2020 By w3codebox
# Author by : www.oldtoolbag.com
# Date : 2020-08-08
from django.shortcuts import get_object_or_404, render
 from django.http import HttpResponseRedirect, HttpResponse
 from django.core.urlresolvers import reverse
 from models import Choice, Question
 # ...
 def vote(request, question_id):
     question = get_object_or_404(Question, pk=question_id)
     try:
         selected_choice = question.choice_set.get(pk=request.POST['choice'])
     except (KeyError, Choice.DoesNotExist):
         # Redisplay the question voting form.
         return render(request, 'polls/detail.html', {
             'question': question,
             'error_message': "You didn't select a choice.",
         )
     else:
         selected_choice.votes += 1
         selected_choice.save()
         # Always return an HttpResponseRedirect after successfully dealing
         # with POST data. This prevents data from being posted twice if a
         # user hits the Back button.
         return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

This code includes several things that have not been covered in this tutorial yet:

request.POST is an object similar to a dictionary, allowing you to access submitted data by key name. In this case, request.POST['choice'] returns the ID of the selected choice as a string. The values of request.POST are always strings. 

Note: Django also provides request.GET to access GET data in the same way – but we explicitly use request.POST in our code to ensure that data can only be modified through POST calls.

If POST data does not provide choice, request.POST['choice'] will raise a KeyError exception. The code above checks for the KeyError exception and displays an error message if the form does not provide a choice.

After the choice count increments, the code returns an HttpResponse redirect instead of a normal HttpResponse. HttpResponseRedirect requires a parameter: the URL to which the user will be redirected (see below)-How do we construct the URL in this situation).

As indicated by the Python comments above, an HttpResponse redirect should always be returned after POST data processing is successful

In this example, we use the HttpResponseRedirect constructor and the reverse() function. This function helps to avoid hardcoding URLs in views. This is because we want to control and point to the view's URL pattern's variable parts through the view name. In this case, using the URLconf configuration makes the reverse() call return a string like:

# Filename : example.py
# Copyright : 2020 By w3codebox
# Author by : www.oldtoolbag.com
# Date : 2020-08-08
'/polls/3/results/'

where3is the value of question.id. Then, this redirect URL will call the 'results' view displayed on the last page.

Now visit the URL: http://127.0.0.1:8000/polls/1/ get the results as shown below:   After someone votes on a question, the vote() view redirects to the results page for that question. Let's write this view (polls/views.py):

# Filename : example.py
# Copyright : 2020 By w3codebox
# Author by : www.oldtoolbag.com
# Date : 2020-08-08
from django.shortcuts import get_object_or_404, render
 def results(request, question_id):
     question = get_object_or_404(Question, pk=question_id)
     return render(request, 'polls/results.html', {'question': question})

now, create a polls/results.html (polls/templates/polls/results.html)Template:

# Filename : example.py
# Copyright : 2020 By w3codebox
# Author by : www.oldtoolbag.com
# Date : 2020-08-08
<h2{{{ question.question_text }}}</h2>
 <ul>
 {% for choice in question.choice_set.all %}
     <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
 {% endfor %}
 </ul>
 <a href="{% url 'polls:detail' question.id %}">Vote again?</a>

Now, open the browser and go to /polls/1/ And the questions to be voted on should be updated on the results page each time a vote is cast. If you submit the form without selecting an option, you should see an error message. After selecting an option and submitting, the following result will be displayed:  

Using Generic Views: Less Code is Better

Modify URL Configuration

First, open polls/urls.py and modify as follows:

from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P<pk>[0-9]+)/$, views.DetailView.as_view(), name='detail'),
    url(r'^(?P<pk>[0-9]+)/results/$, views.ResultsView.as_view(), name='results'),
    url(r'^(?P<question_id>[0-9]+)/vote/$, views.vote, name='vote'),
]