English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
在上一节的教程中,我们介绍了 Django的视图,并编写了一个简单的实例。本小节我们将学习网络投票应用程序,并将侧重于简单的表单处理,以最少代码代码量来实现。
让我们更新 poll detail 模板(“polls/detail.html) ,从上个教程,在模板 polls/templates/polls/detail.html 包含一个HTML<form>元素:
# Filename : example.py # Copyright : 2020 By w3codebox # Author by : fr.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" /> </form>上面的模板显示每个问题选择一个单选按钮。每个单选按钮的值相联问题的选择编号。每个单选按钮的名称是“choice”。这意味着,当有人选择了其中一个单选按钮并提交表单,它会发送POST数据choice=#,其中#是被选择的选择的ID。这是HTML表单的基本概念。 我们设置表单的动作 {% url 'polls:vote' question.id %}, 以及设置 method="post". 使用 method="post" (相对于 method="get") 是非常重要的,因为提交此表将改变服务器端数据的行为。当创建一个改变数据服务器端表单形式,使用 method="post". 这篇文章并不是只针对 Django; 这是一个很好的 Web 开发实践。 forloop.counter表示表单标签通过多少次循环了 因为我们正在创建一个POST形式(可以有修改数据的影响),我们需要担心跨站点请求伪造。但是也不必担心,因为Django自带了保护对抗的一个非常容易使用的系统。总之,这是针对内部URL所有的POST形式应该使用{%csrf_token%}模板标签。
我们还创建了一个虚拟实现 vote() 函数。现在创建一个实用的版本。将以下代码添加到文件 polls/views.py:
polls/views.py 文件的内容如下:
# Filename : example.py # Copyright : 2020 By w3codebox # Author by : fr.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): # Redisplayez le formulaire de vote pour la question. return render(request, 'polls/detail.html', { 'question': question, 'error_message': "Vous n'avez pas sélectionné de choix.", ) else: selected_choice.votes += 1 selected_choice.save() # Toujours renvoyer un HttpResponseRedirect après avoir traité avec succès # avec des données POST. Cela prévient la soumission deux fois des données si un # utilisateur appuie sur le bouton Retour. return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
Ce code contient plusieurs éléments qui n'ont pas encore été abordés dans ce tutoriel :
request.POST est un objet similaire à un dictionnaire, qui vous permet d'accéder aux données soumises par le biais du nom de clé. Dans ce cas, request.POST['choice'] renvoie l'ID du choix sélectionné sous forme de chaîne. Les valeurs de request.POST sont toujours des chaînes.
Attention : Django propose également request.GET pour accéder aux données GET de la même manière - mais nous utilisons explicitement request.POST dans notre code pour nous assurer que les données ne peuvent être modifiées que par l'appel POST.
Si les données POST ne fournissent pas de choix, request.POST['choice'] déclenche une exception KeyError. Le code ci-dessus vérifie l'exception KeyError et affiche un message d'erreur pour le formulaire, s'il n'est pas fourni choice
Après que le compteur de choix a été incrémenté, le code renvoie HttpResponse redirigeant, au lieu d'un HttpResponse normal. HttpResponseRedirect nécessite un paramètre : l'URL vers laquelle l'utilisateur sera redirigé (voir ci-dessous-我们如何构建在这种情况下的URL)。
如上Python的注释所指出的,应该总是在 POST 数据处理成功 后返回一个HttpResponse重定向。
在本示例中我们使用的是 HttpResponseRedirect 构造reverse()函数。此函数有助于避免硬编码URL在视图中。这是因为我们想通过控制并指向该视图的URL模式的可变部分的视图的名称。在这种情况下,使用 URLconf 配置使 reverse()调用返回字符串如:
# Filename : example.py # Copyright : 2020 By w3codebox # Author by : fr.oldtoolbag.com # Date : 2020-08-08 '/polls/3/results/'
其中3是question.id的值。然后,这个重定向的URL将调用“results”视图中显示的最后一页。
现在访问网址:http://127.0.0.1:8000/polls/1/ 得到结果如下所示: 当有人在一个问题投票后,vote() 视图重定向到该问题的结果页面。让我们编写这个视图(polls/views.py):
# Filename : example.py # Copyright : 2020 By w3codebox # Author by : fr.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})
现在,创建一个 polls/results.html (polls/templates/polls/results.html)模板:
# Filename : example.py # Copyright : 2020 By w3codebox # Author by : fr.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>
现在,在浏览器中打开 /polls/1/ 并表决的问题。应该会被每次投票时看到更新结果页。如果您提交表单不选择一个选项,应该看到错误消息。 选择选项,提交后显示如下结果:
首先,打开 polls/urls.py 并修改如下:
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'), ]