编写你的第一个Django应用程序,第4部分

写一个简单的表格¶
让我们从上一个教程更新我们的民意调查详细信息模板(“polls / detail.html”),以便模板包含一个HTML 元素:

民调/模板/调查/ detail.html ¶

{{ question.question_text }}

{% if error_message %}

{{ error_message }}

{% endif %} {% csrf_token %} {% for choice in question.choice_set.all %}
{% endfor %} 快速简要说明:

上面的模板显示每个问题选择的单选按钮。的 value每个单选按钮的是相关联的问题的选择的ID。的 name每个单选按钮的是"choice"。这意味着,当某人选择其中一个单选按钮并提交表单时,它将发送POST数据choice=#,其中#是所选选项的ID。这是HTML表单的基本概念。
我们将表单设置action为,然后设置。使用(而不是 )非常重要,因为提交此表单的行为将改变数据服务器端。每当您创建一个改变数据服务器端的表单时,请使用。这个提示不是Django特有的; 这只是一个很好的Web开发实践。{% url ‘polls:vote’ question.id %}method="post"method="post"method="get"method=“post”
forloop.counter表示for标记经过循环的次数
由于我们正在创建一个POST表单(可以具有修改数据的效果),我们需要担心跨站点请求伪造。值得庆幸的是,您不必太担心,因为Django带有一个非常易于使用的系统来防范它。简而言之,所有针对内部URL的POST表单都应使用 模板标记。{% csrf_token %}
现在,让我们创建一个Django视图来处理提交的数据并对其进行处理。请记住,在教程3中,我们为包含以下行的民意调查应用程序创建了一个URLconf:

民调/的urls.py ¶
path(‘int:question_id/vote/’, views.vote, name=‘vote’),
我们还创建了该vote()函数的虚拟实现。让我们创建一个真实版本。将以下内容添加到polls/views.py:

民调/的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,)))
此代码包含我们在本教程中尚未涉及的一些内容:

request.POST是一个类似字典的对象,允许您按键名访问提交的数据。在这种情况下, request.POST[‘choice’]返回所选选项的ID,作为字符串。request.POST值始终是字符串。

请注意,Django还提供request.GET了以相同方式访问GET数据 - 但我们request.POST在代码中明确使用,以确保数据仅通过POST调用进行更改。

request.POST[‘choice’]KeyError如果 choicePOST数据中没有提供,则会引发。上面的代码检查 KeyError并重新显示问题表单,如果choice没有给出错误消息。

增加选择计数后,代码返回 HttpResponseRedirect而不是正常 HttpResponse。 HttpResponseRedirect采用一个参数:用户将被重定向到的URL(在这种情况下,请参阅以下关于我们如何构造URL的点)。

正如上面的Python注释指出的那样,HttpResponseRedirect在成功处理POST数据之后应该总是返回一个 。这个提示不是Django特有的; 这只是一个很好的Web开发实践。

我们在这个例子中使用构造reverse()函数中的 HttpResponseRedirect函数。此功能有助于避免在视图功能中对URL进行硬编码。它给出了我们想要将控制权传递给的视图的名称以及指向该视图的URL模式的可变部分。在这种情况下,使用我们在教程3中设置的URLconf ,此reverse()调用将返回一个类似的字符串

‘/polls/3/results/’
其中的3是值question.id。然后,此重定向的URL将调用’results’视图以显示最终页面。

如教程3中所述,request是一个 HttpRequest对象。有关HttpRequest对象的更多信息 ,请参阅请求和响应文档。

在某人投票问题后,该vote()视图会重定向到问题的结果页面。让我们写下这个观点:

民调/的views.py ¶
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})
这几乎detail()与教程3中的视图完全相同。唯一的区别是模板名称。我们稍后会修复此冗余。

现在,创建一个polls/results.html模板:

polls / templates / polls /results.html¶

{{ question.question_text }}

  • {% for choice in question.choice_set.all %}
  • {{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
  • {% endfor %}

Vote again?
现在,转到/polls/1/您的浏览器并在问题中投票。您应该会看到每次投票时都会更新的结果页面。如果您在未选择的情况下提交表单,则应该看到错误消息。

注意

我们vote()视图的代码确实存在一个小问题。它首先selected_choice从数据库中获取对象,然后计算新值votes,然后将其保存回数据库。如果您网站的两个用户尝试在同一时间投票,则可能会出错:将检索相同的值,例如42 votes。然后,对于两个用户,计算并保存新值43,但是44将是期望值。

这被称为竞争条件。如果您有兴趣,可以阅读 使用F()来避免竞争条件,以了解如何解决此问题。

使用通用视图:更少的代码更好¶
在detail()(从教程3)和results() 意见是非常简单的-并且如上面提到的,冗余的。index() 显示民意调查列表的视图类似。

这些视图代表了基本Web开发的常见情况:根据URL中传递的参数从数据库获取数据,加载模板并返回呈现的模板。因为这是如此常见,Django提供了一种称为“通用视图”系统的快捷方式。

通用视图将常见模式抽象到您甚至不需要编写Python代码来编写应用程序的程度。

让我们将我们的民意调查应用程序转换为使用通用视图系统,这样我们就可以删除一堆自己的代码。我们只需要采取一些步骤进行转换。我们会:

转换URLconf。
删除一些旧的,不需要的视图。
基于Django的通用视图引入新视图。
继续阅读以了解详情。

为什么代码洗牌?

通常,在编写Django应用程序时,您将评估通用视图是否适合您的问题,并且您将从一开始就使用它们,而不是在中途重构代码。但本教程有意集中于直到现在“以艰难的方式”编写视图,专注于核心概念。

在开始使用计算器之前,您应该先了解基本数学。

修改URL配置¶
首先,打开polls/urls.pyURLconf并将其更改为:

民调/的urls.py ¶
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>为。

修改意见¶
接下来,我们将删除我们的老index,detail和results 视图,并使用Django的通用视图代替。为此,请打开 polls/views.py文件并进行更改,如下所示:

民调/的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和 DetailView。这两个视图分别抽象出“显示对象列表”和“显示特定类型对象的详细页面”的概念。

每个通用视图都需要知道它将采用什么模型。这是使用model属性提供的。
该DetailView通用视图预计从URL中捕获的主键值被调用 “pk”,所以我们已经改变question_id,以pk用于通用视图。
默认情况下,DetailView通用视图使用名为的模板。在我们的例子中,它将使用模板。该 属性用于告诉Django使用特定的模板名称而不是自动生成的默认模板名称。我们还为列表视图指定了- 这确保了结果视图和详细视图在渲染时具有不同的外观,即使它们都是 幕后的。/_detail.html"polls/question_detail.html"template_nametemplate_nameresultsDetailView

同样,ListView通用视图使用一个名为的默认模板; 我们用来告诉 使用我们现有的 模板。/_list.htmltemplate_nameListView"polls/index.html"

在本教程的前几部分中,为模板提供了包含question和latest_question_list 上下文变量的上下文。对于DetailView该question自动提供的变量-因为我们使用的Django模型(Question),Django的是能够确定一个适当的名称为上下文变量。但是,对于ListView,自动生成的上下文变量是 question_list。要覆盖它,我们提供context_object_name 属性,指定我们要使用的属性latest_question_list。作为替代方法,您可以更改模板以匹配新的默认上下文变量 - 但只是告诉Django使用您想要的变量要容易得多。

运行服务器,并使用基于通用视图的新轮询应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值