简单的表单

一个简单的表单

让我们更新一下投票详细页面的模板 polls/detail.html,让它包含一个HTML <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>
  • 设置表单的 action 为 {% url 'polls:vote' question.id %}。method="post",这个提交表单的行为会改变服务器端的数据。
  • 所有针对内部URL的 POST 表单都应该使用 {% csrf_token %} 模板标签。
  • 在 Question 的每个 Choice 前添加一个单选按钮。每个单选按钮的 value 属性是对应的各个 Choice 的 ID。每个单选按钮的 name 是 "choice" 。这意味着,当有人选择一个单选按钮并提交表单时,将发送一个 POST 数据 choice=# ,其中# 为选择的 Choice 的 ID。
  • forloop.counter 指示 for 标签已经循环多少次。

现在,让我们来创建一个 Django 视图来处理提交的数据。我们为polls应用创建了一个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):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
  • request.POST 是一个类字典对象,可以通过关键字的名字获取提交的数据。在这个例子中,request.POST['choice'] 以字符串形式返回选择的 Choice 的 ID。 request.POST 的值永远是字符串。如果在 request.POST['choice'] 数据中没有提供 choice , POST 将引发一个 KeyError 。上面的代码检查 KeyError ,如果没有给出 choice 将重新显示 Question 表单和一个错误信息。
  • HttpResponseRedirect 只接收一个参数:用户将要被重定向的 URL。在这个例子中,我们在 HttpResponseRedirect 的构造函数中使用 reverse() 函数,需要我们给出想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。这个函数避免了我们在视图函数中硬编码 URL。

reverse() 调用将返回一个这样的字符串:'/polls/3/results/'。其中 3 是 question.id 的值。重定向的 URL 将调用 results 视图来显示最终的页面。

当有人对 Question 进行投票后, vote() 视图将请求重定向到 Question 的结果界面。让我们来编写这个视图:

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/ 然后为 Question 投票。你应该看到一个投票结果页面,并且在你每次投票之后都会更新。如果你提交时没有选择任何 Choice,会看到错误信息。

使用通用视图

detail() 和 results() 视图都很简单,但是存在冗余问题。

这些视图反映基本的 Web 开发中的一个常见情况:根据 URL 中的参数从数据库中获取数据、载入模板文件然后返回渲染后的模板。 由于这种情况特别常见,Django 提供一种快捷方式,叫做“通用视图”系统。

通用视图将常见的模式抽象化,可以使你在编写应用时甚至不需要编写Python代码。

我们将polls应用转换成使用通用视图系统,可以删除许多代码,仅仅需要做以下几步来完成:

1. 转换 URLconf;

2. 删除旧的,不再需要的视图;

3. 基于通用视图引入新的视图。

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

改良视图

删除旧的 indexdetail, 和 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 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):
    return HttpResponse("You're voting on question %s." % question_id)

我们在这里使用两个通用视图:ListViewDetailView。这两个视图分别抽象"显示一个对象列表"和"显示一个特定类型对象的详细信息页面"

  • 每个通用视图需要知道它将作用于哪个模型。这由model属性提供。
  • DetailView 期望从URL中捕获名为 pk 的主键值,所以我们把 question_id 改成 pk 。

 

猜你喜欢

转载自www.cnblogs.com/yutb/p/10570654.html