Django 2.1.6.学习初级篇(4)

 

目录

No. 1 写一个简单的表单

No. 2 使用通用视图:代码越少越好

--->修改URLconf

--->修改views


No. 1 写一个简单的表单

让我们从Django 2.1.6.学习初级篇(3)中更新我们的poll detail模板(" polls/detail. HTML "),使模板包含一个HTML <form>元素:

polls/templates/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>

上面模板就是python和HTML语言的结合(是时候去了解一下html)。

我们将表单的重定向URL表达为action="{% url  'polls:vote'  question.id %}",我们设置方法="post"。使用method="post"(相对于method="get")非常重要,因为提交此表单的行为将更改服务器端数据。无论何时创建更改服务器端数据的表单,请使用method="post"。这个技巧不是Django特有的;这只是一个好的Web开发实践。

forloop.counter指示for标记执行循环的次数

由于我们正在创建一个POST表单(它具有修改数据的效果),我们需要担心跨站点请求伪造(敲黑板,知识点)。幸运的是,您不必过于担心,因为Django提供了一个非常易于使用的系统来保护您免受其害。简而言之,所有针对内部url的POST表单都应该使用{% csrf_token %}模板标记。

现在,让我们创建一个Django视图来处理提交的数据并对其进行处理。请记住,在教程3中,我们为polls应用程序创建了一个URLconf,其中包括这一行:

polls/urls.py

path('<int:question_id>/vote/', views.vote, name='vote'),

向poll /view .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['choice']:是一个类字典的对象,允许您通过键名访问提交的数据。在这种情况下,请求。POST['choice']以字符串形式返回所选选项的ID请求。POST值总是字符串。     请注意,Django还提供了GET请求,用于以相同方式访问GET数据,但我们使用的是request。在我们的代码中POST,以确保仅通过POST调用更改数据。
  • 如果POST数据中没有提供选项,POST['choice']将引发KeyError。上面的代码检查KeyError并在没有给出选项的情况下重新显示带有错误消息的问题表单。
  • 在else下面,代码返回一个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”视图来显示最终页面。

在某人对某个问题进行投票之后,vote()视图将重定向到该问题的结果页面。我们来写这个视图:

polls/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})

然后,编写results.html模板:

polls/templates/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/并对问题进行投票。您应该会看到在每次投票时都会更新的结果页面。如果您提交表单而没有选择,您应该会看到错误消息(如果没有投票选项,那么你可以将模型中Choice迁移到后台管理,然后在后台增加你要投票的选项)。

值得注意的是:

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

这叫做竞态条件,后续不提到如何解决。

No. 2 使用通用视图:代码越少越好

像index()这样的视图,根据URL中传递的参数从数据库获取数据,加载模板并返回呈现的模板。因为这很常见,Django提供了一个快捷方式,称为“通用视图”系统。

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

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

1,更改URLconf

2,删除一些旧的、不需要的视图

3,根据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>。

--->修改views

接下来,我们将删除旧的索引、详细信息和结果视图,转而使用Django的通用视图。为此,请打开polls/views.py文件,并改变它如下:

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。

template_name属性用于告诉Django使用特定的模板名

对于DetailView,问题变量是自动提供的——因为我们使用的是Django模型(问题),Django能够为上下文变量确定适当的名称。但是,对于ListView(表示对象列表的页面,后文介绍,这里可以看做IndexView),自动生成的上下文变量是question_list。为了覆盖它,我们提供了context_object_name属性,指定要使用latest_question_list。作为一种替代方法,您可以更改模板以匹配新的默认上下文变量——但是Django会让你使用想要的变量变得容易。

运行服务器,并基于【通用视图】使用新的polls应用程序。有关【通用视图】系统的详细信息,后续介绍

 

当您熟悉表单和通用视图时,请阅读Django 2.1.6.学习初级篇(5),了解如何测试我们的polls应用程序

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值