python 的web框架 django 的入门教程 4 表单

本文是在 python 的web框架 django 的入门教程 3 上开始的。根据 django 官方教程 学习而来,内容也与其教程4配对。

下一节内容是:python 的web框架 django 的入门教程 5 测试(待完成)

本章主要介绍表单和通用视图。

写个简单的表单

修改模板:polls/detail.html

<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><br>
{% endfor %}
<input type="submit" value="Vote">
</form>

简短总结下:

上面的模板为每个问题选择显示一个单选按钮。每个单选按钮的值是相关问题选择的ID。每个单选按钮的名称为“选择”。这就是说,当某人选择一个单选按钮并提交表单时,它将发送POST数据choice =#,其中#是所选选项的ID。这是HTML表单的基本概念。
将表单的action设置为{%url'polls:vote'question.id%},设置method =“ post”。使用method =“ post”(而不是method =“ get”)非常重要,因为提交此表单的行为将改变服务器端的数据。创建更改数据服务器端的表单时,使用method =“ post”。
forloop.counter 表示 for循环 的次数,第1次循环为1,然后是2,3
由于我们正在创建POST表单(可能具有修改数据的效果),因此我们需要担心跨站点请求伪造。值得庆幸的是,不必太担心,因为Django随附了一个非常易用的系统来防御它。所有针对内部URL的POST表单都应使用{%csrf_token%}模板标记。
表单提交为vote,下面修改vote。

修改polls/view.py 中的vote()函数

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值始终是字符串。

如果POST数据中未提供选择,则request.POST ['choice']将引发KeyError。上面的代码检查KeyError并在未给出选择的情况下重新显示带有错误消息的问题表格。

增加选择计数后,代码将返回HttpResponseRedirect而不是常规的HttpResponse。 HttpResponseRedirect接受一个参数:将用户重定向到的URL(有关在这种情况下我们如何构造URL的信息,请参见以下几点)。

正如上面的Python注释所指出的那样,在成功处理POST数据后,应该始终返回HttpResponseRedirect。本技巧并非专门针对Django;这只是Web开发的好习惯。

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

'/polls/3/results/'

其中3是question.id的值。 然后,此重定向的URL将调用“结果”视图以显示最终页面。

下面我们要建立results view。 

修改polls/view.py 中的results()函数:

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 

<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>

投票表单页面detail:

 

投票结果显示页面results: 

投票页面里什么也不选,直接vote,出现报错页面:

通用视图:减少代码

detail()和results()视图非常简单,却很冗余。 显示投票列表的index()视图与此类似。

这些视图代表了基本Web开发的一种常见情况:根据URL中传递的参数从数据库获取数据,加载模板并返回渲染的模板。 因为这很常见,所以Django提供了一个捷径,称为“通用视图”。

通用视图将常见的模式抽象为甚至不需要编写Python代码即可编写应用程序的地步。

让我们将polls程序转换为使用通用视图系统,删除一堆自己的代码。 我们只需要采取一些步骤即可进行转换。 我们会:

转换URLconf。
删除一些旧的不需要的视图。
根据Django的通用视图引入新视图。

修改URLconf

修改polls/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>更改为<pk>。

修改view

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

每个通用视图都需要知道它将作用于什么模型。这是使用model属性提供的。
DetailView通用视图期望将从URL捕获的主键值称为“ pk”,因此我们已将通用视图的question_id更改为pk。
默认情况下,DetailView通用视图使用名为<应用程序名称> / <模型名称> _detail.html的模板。在我们的例子中,它将使用模板“ polls / question_detail.html”。 template_name属性用于告诉Django使用特定的模板名称,而不是自动生成的默认模板名称。我们还为结果列表视图指定了template_name,这可以确保结果视图和局部视图在呈现时具有不同的外观,即使它们都是后台的DetailView。

同样,ListView通用视图使用一个名为<app name> / <model name> _list.html的默认模板;我们使用template_name告诉ListView使用我们现有的“ polls / index.html”模板。

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

修改完后检查运行效果。效果还是一样,只是代码简单了。

本章内容就到此为止。如果对表单form 和通用视图view 比较有感觉了,可以进入下一章:python 的web框架 django 的入门教程 5

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章