【譯】本身動手寫Django app,第四部分【全劇終】

原文地址:https://docs.djangoproject.com/en/1.4/intro/tutorial04/ html

這個教程是從教程3剩下的地方開始的。咱們繼續網絡調查應用程序並將關注簡單的表單處理和精簡咱們的代碼。 python

1、寫一個簡單的表單 數據庫

讓咱們從上個教程中更新咱們的調查詳細模板(「polls/detail.html」),這樣模板就包含一個HTML<form>元素: django

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

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

<form action="/polls/{{ poll.id }}/vote/" 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 }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
快速摘要:

    上面的模板在每一個調查選擇上顯示一個單選按鈕。每一個單選按鈕的值和調查選擇的ID相關聯。每一個單選按鈕的名字就是「choice」。這就是說,當有人選擇一個單選按鈕而後提交表單,你將發送POST數據choice=3。這就是HTML表單101; 瀏覽器

    咱們把表單的動做指向/polls/{{ poll.id }}/vote/,咱們設置method="post"。用method="post"(相反的是method="get")很重要,由於提交這個表單的動做會改變數據的服務器端。無論何時你建立了一個改變數據服務器端的表單,使用method="post"。這個小知識不只僅對Django有用。這是好的網絡開發經驗; 緩存

    forloop.counter暗示for標籤在它的循環中經歷了多少次; 服務器

    既然咱們建立了一個POST表單(它有修改數據的影響),咱們須要擔憂跨站點請求僞造。可是高興的是,你不須要擔憂太多,由於Django提供了一個很容易使用的系統來抵禦它。簡而言之,內部URLs針對的全部POST表單應該用{% csrf_token %}模板標籤; 網絡

{% csrf_token %}標籤須要來自請求對象的信息,它一般從模板環境不可接觸。爲了修復這個,咱們須要對detail視圖作小的調整,讓它看起來像下面同樣: app

from django.template import RequestContext
# ...
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/detail.html', {'poll': p},
                               context_instance=RequestContext(request))
它如何工做的細節在RequestContext文檔中已經解釋了。

如今,讓咱們建立一個處理提交數據的Django視圖並對它作一些事情。記住,在教程3中,咱們爲調查應用程序建立一個URLconf包含了下面的一行: 框架

(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
咱們也建立了一個假的實現vote()函數。讓咱們建立一個真的版本,在polls/views.py中增長下面:
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.template import RequestContext
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_to_response('polls/detail.html', {
            'poll': p,
            'error_message': "You didn't select a choice.",
        }, context_instance=RequestContext(request))
    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.views.results', args=(p.id,)))
這個代碼包含了一些咱們在這個教程中尚未設計到的東西:

    request.POST是一個像字典的對象讓你能夠按照鍵名訪問提交的數據。這種狀況下,request.POST['choice']返回選中的選擇的ID做爲一個字符串。request.POST的值老是一個字符串。注意Django老是用相同的的方式提供request.GET訪問GET的數據——可是咱們在咱們的代碼中明確使用request.POST來確保數據經過POST調用改變了。

    若是POST數據沒有提供choice,request.POST['choice']會產生KeyError。上面的代碼檢查KeyError,若是choice沒有,就會從新顯示一個帶有錯誤信息的調查表單;

    在這個例子中,咱們在HttpResponseRedirect容器中用reverse()函數。這個函數幫助避免在視圖函數中處理硬編碼。這是考慮到的視圖名稱,咱們想經過控制和可變部分的URL模式來指出這個視圖。在這種狀況下,用我麼你在教程3中建立的URLconf,這個reverse()函數將會返回一個像下面的字符串:

'/polls/3/results/'
——這裏的3是p.id的值。這個重定向URL會調用'results'視圖來顯示最終的頁面。注意這兒你須要用視圖的全名(包括前綴)。

在教程3中已經提到了,request是一個HttpResponse對象。更多關於HttpResponse對象,請看請求和響應文檔。

當有人投票了一個調查以後,vote()視圖重定向調查結果頁面。讓咱們寫這個視圖:

def results(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/results.html', {'poll': p})
這基本和教程3中的detail()視圖是徹底同樣。惟一的區別就是名字。咱們以後會修復這個冗餘。

如今,建立一個results.html模板:

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

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

<a href="/polls/{{ poll.id }}/">Vote again?</a>
如今,在你的瀏覽器中查看/polls/1/而後再調查程序中投票。你應該看到一個更新每次你投票的結果頁面。若是你沒有作出選擇就提交了表單,你應該能看到錯誤信息。

2、使用通用視圖:代碼越少越好

detail()(來自教程3)和results()視圖很蠢很簡單——就像上面提到的同樣,冗餘。index()(來自教程3)視圖相似顯示一系列調查。

這些視圖表示基本網絡開發的常例:根據URL傳遞的參數從數據庫得到數據,載入模板,而後返回提供的模板。由於這是共同的,Django提供一個快捷方式叫作「通用視圖」系統。

通用視圖把通常的模式抽象到你不須要寫python代碼的程度。

讓咱們用通用視圖系統轉換咱們的調查程序,這樣咱們就能刪掉許多咱們的代碼。咱們只要採起一些步驟來實現這個轉換。咱們會:

    1.轉換URLconf;

    2.刪除一些老的不須要的視圖;

    3.修復處理新的視圖的URL;

看看詳情。

(爲何攪亂代碼?:一般來說,當寫一個Django應用程序時,你須要估計通用視圖是否適用於你的問題,你將在開始用它們而不是半途重構你的代碼。可是這個教程直到如今的目的關注「硬方式」寫視圖,關注核心理念。在你開始用計算器以前你應該瞭解基本的數學。)

首先,打開poll/urls.py的URLconf。根據到目前的教程,它看起來像這樣:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('polls.views',
    url(r'^$', 'index'),
    url(r'^(?P<poll_id>\d+)/$', 'detail'),
    url(r'^(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)
把它改爲下面這樣:
from django.conf.urls import patterns, include, url
from django.views.generic import DetailView, ListView
from polls.models import Poll

urlpatterns = patterns('',
    url(r'^$',
        ListView.as_view(
            queryset=Poll.objects.order_by('-pub_date')[:5],
            context_object_name='latest_poll_list',
            template_name='polls/index.html')),
    url(r'^(?P<pk>\d+)/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/detail.html')),
    url(r'^(?P<pk>\d+)/results/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/results.html'),
        name='poll_results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
)
在這裏咱們使用了兩個通用視圖:ListView和DetailView。分別地,這兩個視圖抽象「顯示一些列對象」和「顯示一個對象類型的詳細頁面」的概念。

    每一個通用視圖須要知道它做用的模型。這是用模型參數提供的;

    DetailView通用視圖期待從URL中捕獲的第一個關鍵字的值(被稱做「px」),所以咱們爲了通用視圖把poll_id改爲px;

    咱們已經給結果視圖增長了一個名字poll_results,這樣咱們以後有一種提到它的URL方式(詳細信息請查看命名URL模式文檔)。這裏咱們也從django.conf.urls調用url()函數。當你提供一個像這樣模式的名字的時候,使用url()是個好習慣。

默認狀況下,DetailView通用視圖用一個叫作<app name>/<model name>_detail.html模板。在咱們的例子中,它用一個模板「polls/poll_detail.html」。template_name參數是用來告訴Django一個明確的模板名字而不是一個自動生成的默認模板名字。咱們也規定resultstemplate_name的列表視圖——這確保結果視圖和細節視圖在顯示的時候有不一樣的外觀,即便他們幕後都是DetailView。

相似地,ListView通用視圖用一個叫作<app name>/<model name>_list.html的默認模板,咱們用template_name告訴ListView用咱們存在的polls/index.html模板。

在教程的前面部分,已經提供了帶有上下文的模板,它包含了poll和latest_poll_list環境變量。對DetailView來講,poll變量是自動提供的——由於咱們用一個Django模型(Poll),Django能決定環境變量的合適名字。然而,對於ListView來講,自動生成的上下文變量是poll_list。覆寫這個,咱們提供了context_object_name選項來明確咱們想用latest_poll_list。做爲一個可選的方式,你能夠改變你的模板來匹配新的默認的環境變量——可是告訴Django用你想用的變量更容易。

你如今能夠從polls/views.py中刪除index(),detail()和results()。咱們再也不須要他們了——他們已經被通用視圖代替了。

最後要作的是解決通用視圖的使用時URL的處理。在上面的投票視圖中,咱們用reverse()函數避免咱們的URLs硬編碼。如今咱們已經切換到通用視圖,咱們須要改變reverse()調用指向咱們新的通用視圖。咱們不能簡單地使用視圖函數——通用視圖能夠用許屢次——可是咱們能夠用咱們給定的名字:

return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
運行開發服務器,用你基於通用視圖的調查應用程序。

更多關於通用視圖的視覺,請查看通用視圖文檔。

3、即未來臨

這個教程眼下在這結束了。教程更多的安裝將會涉及:

    表單高級處理;

    用RSS框架;

    用緩存框架;

    用評價框架;

    高級管理特徵:權限;

    高級管理特徵:定製JavaScript;

同時,你可能想查看一些點,從這裏到哪裏去。

教程4結束!

整個教程結束,明天開始Django-cms的官方文檔!

相關文章
相關標籤/搜索