【譯】本身動手寫Django app,第三部分【完】

原文地址:https://docs.djangoproject.com/en/1.4/intro/tutorial03/ php

這個教程是從教程2留下的地方開始的。咱們繼續這個基於網絡調查應用程序而且將關注與建立一個公共接口——「view」。 html

1、哲學 python

一個視圖是你Django應用程序中(一種特別的功能和一個特別的模板)「一種」網頁。好比說,在一個網絡博客應用程序中,你可能有下面的視圖: 正則表達式

    博客頁面——顯示最近一些記錄; 數據庫

    進入「細節」頁面——單個記錄的永久連接; express

    基於年存檔的頁面——顯示給定年份帶有記錄的全部月; django

    基於月存檔的頁面——顯示給定月份帶有記錄的全部日; 瀏覽器

    基於日存檔的頁面——顯示給定日期的全部記錄; 安全

    評論動做——處理給定記錄的留言; 服務器

在咱們的調查應用程序中,咱們有下面的四個視圖:

    調查「索引」頁面——顯示最近的一些調查;

    調查「細節」頁面——顯示一個沒有結果可是帶有投票的表單的調查問題;

    調查「結果」頁面——顯示某一個調查的結果;

    投票動做——處理某一個調查的某一個投票;

在Django中,每一個視圖由一個簡單的python函數表示。

2、設計你的URLs

寫視圖的第一步是設計你的URL結構。你經過建立一個叫URLconf的python模塊來實現它。URLconfs是Django把給定的URL和一個給定的python代碼聯繫起來。

當一個用戶要求一個Django支持的頁面,系統查看ROOT_URLCONF設置,它包含了一個python點綴語法的字符串。Django載入那個模塊而後查找一個叫urlpatterns的模塊層變量,它是下列各式的一列元組:

(regular expression, Python callback function [, optional dictionary])
Django從第一個正則表達式開始一直往下搜索,而後一直把請求的URL和每條正則表達式比較,直到找到和他匹配的一個。

當你找到匹配的哪一個是,Django調用一個帶有HttpResponse對象做爲第一個參數的python回調函數,正則表達式中任何「捕獲」的值都做爲關鍵字參數,而且任意關鍵字參數均可以來自字典(一個元組中可選的第三個參數)。

關於更多關於HttpResponse對象的信息,請查看請求和響應對象。更多關於URLconfs,請查看URL分配器。

當你在教程1開頭的地方運行django-admin.pu startproject mysite,系統在mysite/urls.py中建立了一個默認的URLconf。它也自動把你的ROOT_URLCONF設置(在setting.py中)指到那個文件:

ROOT_URLCONF = 'mysite.urls'
是時候來個例子了。編輯mysite/urls.py讓它看起來這樣:
from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^polls/$', 'polls.views.index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
    url(r'^admin/', include(admin.site.urls)),
)
這是一個有價值的回顧。當有人從你的網站請求一個頁面——好比說,「/poll/23/」,Django會載入這個python模塊,由於它被指向了ROOT_URLCONF設置。它找到變量名叫urlpatterns而後按順序穿過正則表達式。當它找打了一個匹配的正則表達式——r'^poll/(?P<poll_id>\d+)/$'——它從polls/views.py中調用一個detail()函數。最後,它像這樣調用detail()函數:
detail(request=<HttpRequest object>, poll_id='23')
poll_id='23'部分來自(?P<poll_id>\d+)。在一個模式周圍用括號來「捕獲」和模式匹配的文本,而後把它做爲一個參數傳遞給視圖函數。?P<poll_id>定義了被用來識別匹配模式的名字,\d+是匹配一列數字的正則表達式。

由於URL模式是一個正則表達式,你對它們作的一切都沒有限制。所以這裏沒有必要增長像.php這樣的URL使人討厭的東西——除非你有一個不同凡響的幽默感,這種狀況下,你能夠這樣作:

(r'^polls/latest\.php$', 'polls.views.index'),
可是,如今不須要這樣作,這看起來真的很蠢。

注意這些正則表達式不搜索GET和POST參數,或者域名。好比說,一個請求:http://www.example.com/myapp/,URLconf將會查找myapp/,在一個請求:http://example.com/myapp/?page=3,URLconf將會查找myapp/。

若是在正則表達式方面須要幫助,請查看維基百科的介紹和re模塊的文檔。同時,Jeffrey Friedl寫的O'Reilly出版的書「掌握正則表達式」也很是精彩。

最後,一個性能注意:這些正則表達式是在你URLconf模塊第一次載入的時候編譯的,它們的速度是很是快的。

3、寫你本身的視圖

不過到目前爲止咱們尚未建立本身的視圖——咱們只是有URLconf。可是咱們讓Django來合適的遵循URLconf。

啓動Django開發網絡服務器:python manage.py runserver。

如今在你的瀏覽器中瀏覽「http://localhost:8000/polls/」。你應該看到下面帶有鮮豔顏色錯誤的頁面:

ViewDoesNotExist at /polls/

Could not import polls.views.index. View does not exist in module polls.views.
這個錯誤出現的緣由是你尚未寫polls/views.py的index函數。

嘗試「/polls/23/」,「/polls/23/results/」和「/polls/23/vote/」都同樣的,錯誤消息告訴你Django沒有找到視圖(查找視圖失敗,由於你尚未寫任何視圖)。

顯示是時候寫第一個視圖了。打開polls/views.py文件而後輸入下面的python代碼:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")
這多是最簡單的視圖。在你的瀏覽器去看看「/polls/」你的文本。

如今讓咱們增長一些更多的視圖。這些視圖有一點不一樣,由於他們帶有一個參數(記住,這是從URLconf中正則表達式捕獲的傳遞進來的):

def detail(request, poll_id):
    return HttpResponse("You're looking at poll %s." % poll_id)

def results(request, poll_id):
    return HttpResponse("You're looking at the results of poll %s." % poll_id)

def vote(request, poll_id):
    return HttpResponse("You're voting on poll %s." % poll_id)

在你的瀏覽器中查看「/polls/34/」,它運行detail()方法然手顯示你在URL中提供的ID。嘗試「/polls/34/results/」和「/polls/34/vote/」也是同樣的——它們顯示結果和投票頁面的佔的地方。

4、寫一個實際能作一些事情的視圖

每一個視圖都作兩件事情中的一件:返回一個包含請求頁面內容的HttpResponse對象,或者產生一個錯誤好比說Http404。剩下的就取決於你。

你的視圖能夠從數據庫讀取記錄,或者不能夠。它能夠用一個模板系統好比說Django自帶的或者第三方python模板系統,或者不能夠。他用你想用的python庫能夠生成一個PDF文件,輸出XML,建立一個動態的ZIP文件,你想的一切均可以。

全部的Django期待的是一個HttpResponse或者是一個溢出。

由於這很方便,讓咱們用Django本身的數據庫API,這個咱們在教程1中已經涉及到了。這兒是index()視圖的一個嘗試,它顯示系統中按照公開時間的最近五個用逗號分開的調查問題:

from polls.models import Poll
from django.http import HttpResponse

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    output = ', '.join([p.question for p in latest_poll_list])
    return HttpResponse(output)
可是這裏有一個問題:視圖中頁面的設計硬編碼的。若是你想改變頁面的外觀,你必須編輯這個python代碼。所以讓咱們用Django的模板系統來設計從python中分離出來:
from django.template import Context, loader
from polls.models import Poll
from django.http import HttpResponse

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    t = loader.get_template('polls/index.html')
    c = Context({
        'latest_poll_list': latest_poll_list,
    })
    return HttpResponse(t.render(c))
這些代碼載入一個叫「polls/index.html」的模板而後把它傳遞給上下文。上下文是一個把模板變量名字映射到python對象的字典。

從新載入這個頁面,如今你會看到一個錯誤:

TemplateDoesNotExist at /polls/
polls/index.html
啊哈,到目前爲止尚未模板。首先在你係統的某個Django能夠接觸到的地方建立一個目錄(無論你運行的是什麼服務器,Django都運行同樣的)。可是不要把它放在你的根文檔下面。爲安全期間,你不能讓他們公開。而後編輯settings.py中的TEMPLATE_DIRS來高速Django在哪能夠找到模板——就像在教程2「低昂之管理頁面的外觀和感受」的部分。

當你已經作完了這些,在你的模板目錄下建立一個polls目錄。在這個目錄裏面,建立一個叫index.html。注意咱們的loader.get_template('polls/index.html')代碼從上面映射到系統的「[template_directory]/polls/index.html」。

把下面的代碼放到模板裏:

{% if latest_poll_list %}
    <ul>
    {% for poll in latest_poll_list %}
        <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
在瀏覽器中載入頁面,你能夠看到一個包含來自教程1的「What's up」調查的布告欄列表的容器。這個連接指向調查的詳細頁面。

5、一個快捷方式:render_to_response()

用提供模板的結果載入一個模板,填寫上下文,返回一個HttpResponse對象,這些都是很常見的習語。Django提供一個快捷方式。這是咱們的所有重寫的index()的視圖:

from django.shortcuts import render_to_response
from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
注意咱們一旦在全部視圖中這樣作時候,咱們就再也不須要載入loader,Context和HttpResponse。

render_to_response()函數帶有一個模板名做爲它的第一個參數和一個做爲第二個可選參數的字典。它返回一個提供給定上下文的給定模板的HttpResponse對象。

6、產生404錯誤

如今,咱們來處理調查細節的視圖——顯示給定調查的問題。下面是視圖:

from django.http import Http404
# ...
def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404
    return render_to_response('polls/detail.html', {'poll': p})
這裏新的內容:若是你請求一個不存的ID,視圖產生Http404溢出。

咱們後面討論你能夠在polls/detail.html放什麼,可是若是你想快點讓例子運行起來,只要:

{{ poll }}
就能正式運行了。

7、一個快捷方式:get_object_or_404()

用get(),視圖不存在時產生一個Http404錯誤時很常見的習語。Django提供一個快捷方式。下面是重寫的detail()視圖:

from django.shortcuts import render_to_response, get_object_or_404
# ...
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/detail.html', {'poll': p})
get_object_or_404()函數把Django的模型做爲它的第一個參數和一個任意數量的關鍵字參數,這些參數是傳到模塊的get()函數。若是視圖不存在就產生一個Http404錯誤。

(哲學:爲何咱們用一個get_object_or_404()幫助函數而不是在更高的層次自動捕獲ObjectDoesNotExit溢出或者讓模型的API產生Http404而不是ObjectDoesNotExit?由於那樣作會耦合模型層和視圖層。Django的一個最重要的設計就是實現鬆耦合。)

這裏有一個get_list_or_404()函數,他就像和get_object_or_404()同樣工做——除了使用filter()而不是get()。當列表是空的時候就產生Http404錯誤。

8、寫一個404(頁面沒找到)視圖

當你從一個視圖產生Http404錯誤的時候,Django會載入一個致力於處理404錯誤的特殊視圖。它經過在你的根URLconf(只在你的根URLconf下,無論在其餘什麼地方設置handler404都無效)查找變量handler404,它是一個python點綴語法的字符串——通常URLconf回調函數用相同的格式。一個404視圖自己沒有什麼特別的:它只是一個普通的視圖。

通常來講你不須要煩惱寫404視圖。若是你沒有設置handler404,默認狀況下使用內建的django.views.defaults.page_not_found()。這種狀況下,你任然有一件事要作:在根模板目錄建立一個404.html模板。默認的404視圖將會調用適用於全部404錯誤的模板。若是DEBUG被設置成False(在你的設置文件中),而且你沒有建立一個404.html文件,就會產生一個Http500。所以記住建立一個404.html。

關於404視圖耦合更多的東西:

    若是DEBUG被設置成True(在你的設置文件中),你的404視圖將永遠不會被用到(所以404.html模板永不被提供)由於此時顯示的是錯誤的追溯。

    404視圖被稱爲Django在檢查URLconf中正則表達式是沒有找到匹配。

9、寫一個500(服務器錯誤)視圖

相似地,你的根URLconf能夠定義一個handler500,防止服務器錯誤它指向一個視圖。當你視圖代碼有錯誤時,服務器錯誤也可能出現。

10、使用模板系統

回到咱們調查應用程序的detail()視圖。鑑於上下文變量poll,下面是咱們「polls/detail.html」模板可能看起來的樣子:


<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice }}</li>
{% endfor %}
</ul>
模板系統用點查看語法來接觸變量的屬性。在{{ poll.queston }}的例子中,第一個Django在poll對象中查找字典。若是失敗的話,它嘗試屬性查找——在這種狀況下,它有效的。若是屬性查找也失敗了,它將嘗試列表索引查找。


在{% for %}循環中會發生方法調用:poll.choice_set.all被翻譯成python代碼poll.choice_set.all(),它返回Choice對象的迭代而且在{% for %}標籤中適合使用。更多關於模板的消息請查看模板指導。

11、簡化URLconfs

花點時間來處理視圖和模板系統。就像你編輯URLconf同樣,你可能注意到這裏有點冗餘:


urlpatterns = patterns('',
    url(r'^polls/$', 'polls.views.index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
)
也就是說,每一個回調都在polls.views中。


由於這是一個常見的狀況,URLconf框架爲常見的前綴提供一個快捷方式。你能夠分析出常見的前綴而且增長它們做爲patterns()的第一個參數,就像下面:


urlpatterns = patterns('polls.views',
    url(r'^polls/$', 'index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
)
他在功能上可以前的格式是徹底同樣的,它只是作了點整理。


既然你一般不想一個程序的前綴應用到URLconf的每一個回調中,你能夠連接多個patterns()。你mysite/urls.py的所有如今應該看起來是這樣的:


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

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('polls.views',
    url(r'^polls/$', 'index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
)

urlpatterns += patterns('',
    url(r'^admin/', include(admin.site.urls)),
)
12、URLconfs的去耦合


咱們學習他的時候應該花點時間把咱們Django項目配置的調查程序URL去耦合。Django的程序是插件式的——也就是說,每一個特別地程序應該能夠用最小的大驚小怪應用到另外一個Django的安裝。

多虧了python manage.py startapp建立的嚴格目錄結構,咱們的投票程序在這個時候是至關去耦合化的。可是它的一部分是和Django設置耦合的:URLconf。

咱們已經在mysite/urls.py裏編輯,可是一個程序的URL設計只針對一個應用程序,而不是Django的安裝——所以讓咱們在應用程序目錄裏把URLs刪掉。

把mysite/urls.py拷貝到polls/urls.py,而後改變mysite/urls.py去刪除針對調查程序的URLs而後插入一個include(),讓它是這樣的:


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

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^polls/', include('polls.urls')),
    url(r'^admin/', include(admin.site.urls)),
)
include()只是簡單的參照另一個URLconf。注意正則表達式沒有$(匹配結束字符)可是有一個斜槓。當Django面對include()時,它砍掉在這個點上匹配的URL而後把剩下的字符串傳給包含的URLconf進行更多的處理。


當一個用戶訪問系統的「/polls/34/」會發生下面的事情:

    Django會找到一個匹配的'^polls/';

    而後Django會去掉匹配的文本(「polls/」)而後把剩下的文本——「34」——傳遞給'polls.urls'URLconf進行更多的處理;

既然咱們已經去耦合了,咱們須要刪除每行開頭的「polls/」,同時也要刪除註冊管理站點的那一行。你的poll/urls.py文件如今應該看起來是這樣的:

from django.conf.urls import patterns, 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'),
)


include()後面的想法和URLconf去耦合是爲了是讓它容易變成插件和運行的URLs。 調查調查已經在URLconf中,他們能夠被放置在「/polls/」下,或者「/fun_polls/」下,或者「/content/polls/」下,或者任何根路徑,程序一樣能工做。

全部的調查程序關心的是相對路徑,而不是絕對路徑。

當你對寫視圖感到合適的話,讀教程4學習更多關於處理通用視圖。

教程3結束!

相關文章
相關標籤/搜索