django 官方教程翻译-part4

写一个简单的表单
更新detail.html,代码:

<h1>{{ poll.question }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="{% url 'polls:vote' poll.id %}" method="post">
{% csrf_token %}
{% for choice in poll.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>

解释一下上面的代码:
1 上面代码为poll对象的每个choice对应一个单选按钮,每个单选按钮的value就是choice的id号,name为choice,也就是说,按下提交按钮,发送的POST数据是这样的choice=3.
2 设置表单的action为{% url 'polls:vote' poll.id %},method为post.如果需要传递数据的话建议使用post方法.

3 forloop.counter 是for循环计数器

4 这里使用了POST表单,存在跨站点请求伪造的安全问题.值得庆幸的是,不需要你为此担心,django自带一个简单易用的拦截系统,那就是,所有的POST表单都应该使用{% csrf_token %}模板标签.现在,创建一个django试图可以处理这个提交上去的数据.

还记得在polls/urls.py中,这条语句吗?

url(r'^(?P<poll_id>d+)/vote/$', views.vote, name='vote'),


现在创建一个真正的投票功能试图vote(),编辑polls/views.py,代码:

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from polls.models import Choice, Poll
# ...
def vote(request, poll_id):
  p = get_object_or_404(Poll, pk=poll_id)
  try:
    selected_choice = p.choice_set.get(pk=request.POST['choice'])
  except (KeyError, Choice.DoesNotExist):
  # Redisplay the poll voting form.
  return render(request, 'polls/detail.html', {
    'poll': p,
    '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=(p.id,)))

以上代码包含一些内容在之前没有提到过的:
1 request.POST是一个类似与字典的对象,可以通过键名访问到提交的数据,在这里request.POST['choice']返回被选择的choice的id.

注意:django同样也提供了获取get方法提交的数据的request.GET.

2 如果使用的键名不存在,会抛出KeyError的异常,上面的代码已经对这种异常进行处理了:重定向到提交数据的页面,同时显示错误信息.

3 增加相应choice的votes值之后,保存.并返回一个HttpResponseRedirect,而不是HttpResponse,这样会把显示页面重定向到第一个参数指定的url那里.

4 使用POST提交数据成功之后,都应该使用HttpResponseRedirect,这是web最佳实践,不仅仅适用于django.为什么?自己比较一下两者之间的不同吧

5 在HttpResponseRedirect中使用了reverse,这个函数避免的使用url硬编码,允许想模板中那样使用想polls:results这样的url表示方式.reverse返回的字符串类似这样的形式:
'/polls/3/results/'
这个3是p.id的值,这个请求url,会调用results视图来显示最终的页面.
更多关于HttpRequest对象的内容,阅读:https://docs.djangoproject.com/en/1.6/ref/request-response/

现在编辑results视图,代码:

from django.shortcuts import get_object_or_404, render

def results(request, poll_id):
  poll = get_object_or_404(Poll, pk=poll_id)
  return render(request, 'polls/results.html', {'poll': poll})

以上代码几乎与detail视图,一模一样,稍后会解决这个冗余问题
现在,编辑results.html模板,代码:

<h1>{{ poll.question }}</h1>

<ul>
{% for choice in poll.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' poll.id %}">Vote again?</a>

现在访问/polls/1/,然后投票,你会看到每次投票results结果都要更新.如果你不选任何一个choice,然后提交,会看到错误信息.

使用通用模板,减少代码冗余
上面创建的detail(),results(),index()都是那么的相似.这些视图都是根据请求url从数据库中获取数据,然后加载一个模板,然后渲染之后的模板.因此django提供了"generic views"(下面称"通用视图")系统来简化模板的开发.减少代码冗余.

通用视图对通用模式进行抽象,所以你甚至不需要编写一行代码也可以创建一个app.

现在在poll应用中使用通用视图系统,这个就可以把重复的代码去掉了.使用步骤:
1 转换URLconf
2 删除旧的,不必要的视图
3 新的视图使用通用视图
为什么要梳理代码:
通常你在创建一个django app时,会分析通用视图是否能够可以解决你的问题.然后从一开始就使用它,而不是中途重构代码时才引入.本系列教程先是关注如何创建一个app,现在开始注重编程的核心概念.也就是说,你需要有基本的数学基础,才能使用一个计算器.

修改URLconf
编辑polls/urls.py,代码:

from django.conf.urls import patterns, url

from polls import views

urlpatterns = patterns('',
  url(r'^$', views.IndexView.as_view(), name='index'),
  url(r'^(?P<pk>d+)/$', views.DetailView.as_view(), name='detail'),
  url(r'^(?P<pk>d+)/results/$', views.ResultsView.as_view(), name='results'),
  url(r'^(?P<poll_id>d+)/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.core.urlresolvers import reverse
from django.views import generic

from polls.models import Choice, Poll

class IndexView(generic.ListView):
  template_name = 'polls/index.html'
  context_object_name = 'latest_poll_list'

  def get_queryset(self):
  """Return the last five published polls."""
    return Poll.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
  model = Poll
  template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
  model = Poll
  template_name = 'polls/results.html'

def vote(request, poll_id):
....

上面的代码使用了两个通用视图:ListView和DetailView,这两个视图的含义分别是显示对象列表和显示具体的对象.
每个通用视图都需要知道它将对哪个模型进行操作.
DetailView通用视图期望从URL中获取名为"pk"的主键值,因此我们将poll_id变成pk。
默认情况下,DetailView使用的模板命名规则为<app name>/<model name>_detail.html.它对应这里的模板名为“polls/poll_detail.html"。template_name属性是用于告诉django使用指定的模板名,而不是自动生成的默认模板名。在这里也指定了results list view的模板名——确保它们按照自定义的方式渲染视图。
同样的,ListView通用视图,使用默认的模板名规则<app name>/<model name>_list.html.这里使用了template name属性指定了一个模板名“polls/index.html”
在前面的教程中,模板已经提供了包含poll,latest_poll_list上下文变量的上下文。对于DetailView,poll变量是自动提供的-因为正在使用Poll django 模型,而Django能够为上下文变量定义合适的名称。然而,对于ListView,自动生成的上下文变量叫“poll_list”。不过可以通过给context_object_name属性赋值为你想要使用的名字“latest_poll_list”,另外一种解决方案就是把模板中的latest_poll_list改成poll_list,但是第一种方法更加简单和方便。
运行服务器,使用经过通用视图重构过的poll app。
通用视图的更多细节,阅读:https://docs.djangoproject.com/en/1.6/topics/class-based-views/

原文地址:https://www.cnblogs.com/chuan-joker/p/django_tutorials_4.html