编写你的第一个 Django 应用程序,第4部分
本教程从教程 3 停止的地方开始。我们是 继续民意调查应用程序,并将专注于表单处理和 减少我们的代码。
一、编写最小表单
让我们更新上一个教程的投票详细信息模板(“polls/detail.html”) ,以便模板包含一个 HTML <form>元素:
<form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} <fieldset> <legend><h1>{{ question.question_text }}</h1></legend> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} {% 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><br> {% endfor %} </fieldset> <input type="submit" value="Vote"> </form>
- 由于我们正在创建一个 POST 表单(这可能会产生修改的效果 数据),我们需要担心跨站点请求伪造。 值得庆幸的是,你不必太担心,因为 Django 带有一个 有用的系统来防止它。简而言之,所有 POST 表单 定位到内部网址应使用
{% csrf_token %}
模板标记。
现在,让我们创建一个 Django 视图来处理提交的数据并 随之而来的东西。请记住,在教程 3 中,我们 为包含以下行的投票应用程序创建了一个 URLconf:
path("<int:question_id>/vote/", views.vote, name="vote"),
我们之前创建了vote()函数的虚拟实现。让我们 创建一个真实版本。将以下内容添加到:polls/views.py
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls 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,)))
此代码包含一些我们在本教程中尚未介绍的内容:
-
请求。POST
是一个类似字典的 允许您按键名访问提交数据的对象。在本例中,返回所选选项的 ID,作为 字符串。请求。开机自检
值为 始终为字符串。request.POST['choice']
请注意,Django 也提供了
请求。
GET 以相同的方式访问 GET 数据 – 但我们明确使用请求。
在我们的代码中 POST,以确保数据仅 通过开机自检呼叫进行更改。 -
request.POST['choice']
如果开机自检数据中未提供,将引发密钥错误
。上面的代码检查KeyError
并重新显示错误的问题表单 消息(如果未给出)。choice
choice
-
递增选择计数后,代码将返回
HttpResponseRedirect
,而不是正常的HttpResponse
。HttpResponseRedirect
采用单个参数: 用户将被重定向到的 URL(请参阅以下点,了解如何 在这种情况下,我们构造 URL)。正如上面的 Python 注释所指出的,在成功处理 开机自检数据。这个技巧不是特定于 Django 的;这是很好的网络开发 一般做法。
-
在这个例子中,我们在
HttpResponseRedirect
构造函数中使用了reverse()
函数。 此函数有助于避免在视图函数中对 URL 进行硬编码。 它被赋予了我们要向其传递控制权的视图的名称和 指向该视图的 URL 模式的变量部分。在此 案例,使用我们在教程 3 中设置的 URLconf, 这个reverse()
调用将返回一个字符串,例如"/polls/3/results/"
对问题进行投票后,vote() 视图将重定向到结果 问题的页面。让我们改写下这个视图:
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})
这与教程 3 中的 detail() 视图几乎完全相同。唯一的区别是模板名称。我们将解决此问题 以后冗余。
现在,创建一个模板:polls/results.html
<h1>{{ question.question_text }}</h1> <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/ 浏览器中并在问题中投票。您应该会看到一个 每次投票时都会更新的结果页面。如果您提交表格 如果没有选择选项,您应该会看到错误消息。
二、使用通用视图:代码越少越好
detail()(来自教程 3)和 results() 视图非常短 - 如上所述,冗余。显示投票列表的 index() 视图与此类似。
这些视图代表了基本 Web 开发的常见情况:从以下位置获取数据 数据库根据 URL 中传递的参数,加载模板和 返回呈现的模板。
因为这太常见了,Django 提供了一个 快捷方式,称为“通用视图”系统。
通用视图将常见模式抽象到您甚至不需要的程度 编写 Python 代码来编写应用程序。
让我们将我们的投票应用程序转换为使用通用视图系统,以便我们可以删除 一堆我们自己的代码。我们必须采取几个步骤才能进行转换。 我们会的:
- 转换网址。
- 删除一些旧的、不需要的视图。
- 基于 Django 的通用视图引入新视图。
请继续阅读以了解详细信息。
三、修改网址
首先,打开polls/urls.py文件 , URLconf 并像这样更改它:
from django.urls import path from . import views app_name = "polls" urlpatterns = [ path("", views.IndexView.as_view(), name="index"), path("<int:pk>/", views.DetailView.as_view(), name="detail"), path("<int:pk>/results/", views.ResultsView.as_view(), name="results"), path("<int:question_id>/vote/", views.vote, name="vote"), ]
请注意,第二个和 第三种模式已从 <question_id>更改为 <pk>。
四、修改视图
接下来,我们将删除旧的 index、detail 和 results 视图,并使用 Django 的通用视图。为此,请打开 polls/views.py 文件并按如下所示进行更改:
from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Choice, Question class IndexView(generic.ListView): template_name = "polls/index.html" context_object_name = "latest_question_list" def get_queryset(self): """Return the last five published questions.""" return Question.objects.order_by("-pub_date")[:5] class DetailView(generic.DetailView): model = Question template_name = "polls/detail.html" class ResultsView(generic.DetailView): model = Question template_name = "polls/results.html" def vote(request, question_id): ... # same as above, no changes needed.
我们在这里使用两个通用视图: ListView
and DetailView
. 视图
。分别是那些 两个视图抽象化了“显示对象列表”和 “显示特定类型对象的详细信息页面。”
- 每个通用视图都需要知道它将执行什么模型 后。这是使用model属性提供的。
通用视图 期望调用从 URL 称为pk值,因此我们把question_id更改为pk 的通用视图。DetailView
在本教程的前面部分中,提供了模板 具有包含 和 上下文变量的上下文。
对于变量提供 自动 – 因为我们使用的是 Django 模型 (), Django 能够为上下文变量确定适当的名称。
但是,对于 ListView,自动生成的上下文变量为 。为了覆盖它,我们提供了属性,指定我们要改用。
作为替代方法,您可以更改模板以匹配 新的默认上下文变量 - 但告诉 Django 要容易得多 使用所需的变量。
运行服务器,并使用基于通用视图的新网络投票应用。
有关通用视图的完整详细信息,请参阅通用视图文档。
当您熟悉表单和通用视图后,请阅读本文的第 5 部分 教程,了解如何测试我们的民意调查应用程序。
---------------------------------------end--------------------------