【Django第一步】第四部分:表单和通用视图

【Django第一步】第四部分:表单和通用视图

@[Django|翻译| python]

本教程将重点放在表单处理和削减我们的代码量

写一个简单的表单

让我们更新上一教程中的detail模板,使得它包含一个<form>元素:

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

简要说明:
- 面的模板为每个问题选项显示一个单选按钮。每个选项的value是与之相关的每个问题的选项的id,每个选项的name值是”choice”。当有人选择其中一个单选按钮并提交表单时,它将发送POST数据choice=#,其中#是所选选项的ID。这是HTML表单的基本概念。
- 我们设置窗体的action为{% url 'polls:vote' question.id %},方法为post,使用method是非常重要的。
- forloop.counter表示for循环经过的次数
- 由于我们正在创建POST表单(可能会影响修改数据),因此我们需要担心跨站点请求伪造。值得庆幸的是,您不必太担心,因为Django带有一个非常易于使用的系统来保护它。简而言之,所有以内部URL为目标的POST表单都应使用 模板标记。{% csrf_token %}

接下来,我们实现以下真正的vote函数

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
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['choice']如果 在POST数据中没有提供choice,将会引发KeyError。如果choice没有给出,上面的代码会检查 并重新显示问题表单并显示错误消息。
  • 增加选择计数后,代码将返回一个 HttpResponseRedirect而不是一个正常值 HttpResponse。HttpResponseRedirect只有一个参数:用户将被重定向到的URL(关于这种情况下我们如何构造URL,请参阅以下几点)。正如上面的Python注释所指出的,您应该总是HttpResponseRedirect在成功处理POST数据之后返回一个 。这个提示并不是针对Django的; 这只是一个很好的Web开发实践
  • 在这个例子reverse()中,我们在HttpResponseRedirect构造函数中使用了这个函数 。此功能有助于避免在视图功能中硬编码URL。它给出了我们想要传递控制权的视图的名称以及指向该视图的URL模式的可变部分

有人在问题中投票后,该vote()视图会重定向到Question的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>

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

注意:
我们vote()观点的代码确实有一个小问题。它首先selected_choice从数据库获取对象,然后计算新值votes,然后将其保存回数据库。如果您的网站的两个用户在同一时间尝试投票,这可能会出错:相同的值,比方说42,将被检索votes。然后,为两个用户计算并保存43的新值,但44将是预期值。

使用通用视图:代码越少越好

在detail()(从教程3)和results() 意见是非常简单的-并且如上面提到的,冗余的。index() 显示民意调查列表的视图与此类似。

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

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

我们将我们的投票应用程序转换为使用通用视图系统,以便我们可以删除一大堆我们自己的代码。我们只需采取几个步骤即可完成转换。我们会:
- 转换URLconf。
- 删除一些旧的不需要的视图。
- 基于Django的通用视图引入新的视图。

修改url配置

修改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'),
]

修改视图

我们删除老的index,detail,results视图,并使用Django的通用视图代替。为此,我们更改polls/views.py文件如下:

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
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中捕获key为 “pk”,所以我们已经不用question_id,而是以pk用于通用视图。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值