前一章中,咱們解釋瞭如何創建一個 Django 項目並啓動 Django 開發服務器。 在這一章,你將會學到用Django建立動態網頁的基本知識。html
正如咱們的第一個目標,建立一個網頁,用來輸出這個著名的示例信息:python
Hello world.程序員
若是你曾經發布過Hello world頁面,可是沒有使用網頁框架,只是簡單的在hello.html
文本文件中輸入Hello World,而後上傳到任意的一個網頁服務器上。 注意,在這個過程當中,你已經說明了兩個關於這個網頁的關鍵信息: 它包括(字符串 "Hello world"
)和它的URL( http://www.example.com/hello.html
, 若是你把文件放在子目錄,也多是 http://www.example.com/files/hello.html
)。web
使用Django,你會用不一樣的方法來講明這兩件事 頁面的內容是靠view function(視圖函數) 來產生,URL定義在 URLconf 中。首先,咱們先寫一個Hello World視圖函數。正則表達式
在上一章使用django-admin.py startproject
製做的mysite
文件夾中,建立一個叫作views.py
的空文件。這個Python模塊將包含這一章的視圖。 請留意,Django對於view.py的文件命名沒有特別的要求,它不在意這個文件叫什麼。可是根據約定,把它命名成view.py是個好主意,這樣有利於其餘開發者讀懂你的代碼,正如你很容易的往下讀懂本文。數據庫
咱們的Hello world視圖很是簡單。 這些是完整的函數和導入聲明,你須要輸入到views.py
文件:django
from django.http import HttpResponse def hello(request): return HttpResponse("Hello world")
咱們逐行逐句地分析一遍這段代碼:瀏覽器
首先,咱們從
django.http
模塊導入(import)HttpResponse
類。參閱附錄 H 瞭解更多關於HttpRequest
和HttpResponse
的細節。 咱們須要導入這些類,由於咱們會在後面用到。服務器接下來,咱們定義一個叫作
hello
的視圖函數。cookie每一個視圖函數至少要有一個參數,一般被叫做
request
。 這是一個觸發這個視圖、包含當前Web請求信息的對象,是類django.http.HttpRequest
的一個實例。在這個示例中,咱們雖然不用request
作任何事情,然而它仍必須是這個視圖的第一個參數。注意視圖函數的名稱並不重要;並不必定非得以某種特定的方式命名才能讓 Django 識別它。 在這裏咱們把它命名爲:hello,是由於這個名稱清晰的顯示了視圖的用意。一樣地,你能夠用諸 如:hello_wonderful_beautiful_world,這樣難看的短句來給它命名。 在下一小節(Your First URLconf),將告訴你Django是如何找到這個函數的。
這個函數只有簡單的一行代碼: 它僅僅返回一個HttpResponse對象,這個對象包含了文本「Hello world」。
這裏主要講的是: 一個視圖就是Python的一個函數。這個函數第一個參數的類型是HttpRequest;它返回一個HttpResponse實例。爲了使一個Python的函數成爲一個Django可識別的視圖,它必須知足這兩個條件。 (也有例外,可是咱們稍後纔會接觸到。
如今,若是你再運行:python manage.py runserver,你還將看到Django的歡迎頁面,而看不到咱們剛纔寫的Hello world顯示頁面。 那是由於咱們的mysite項目還對hello視圖一無所知。咱們須要經過一個詳細描述的URL來顯式的告訴它而且激活這個視圖。 (繼續咱們剛纔相似發佈靜態HTML文件的例子。如今咱們已經建立了HTML文件,但尚未把它上傳至服務器的目錄。)爲了綁定視圖函數和URL,咱們使用URLconf。
URLconf 就像是 Django 所支撐網站的目錄。 它的本質是 URL 模式以及要爲該 URL 模式調用的視圖函數之間的映射表。 你就是以這種方式告訴 Django,對於這個 URL 調用這段代碼,對於那個 URL 調用那段代碼。 例如,當用戶訪問/foo/時,調用視圖函數foo_view(),這個視圖函數存在於Python模塊文件view.py中。
前一章中執行 django-admin.py startproject
時,該腳本會自動爲你建了一份 URLconf(即 urls.py
文件)。 默認的urls.py會像下面這個樣子:
from django.conf.urls.defaults import * # Uncomment the next two lines to enable the admin: # from django.contrib import admin # admin.autodiscover() urlpatterns = patterns('', # Example: # (r'^mysite/', include('mysite.foo.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: # (r'^admin/', include(admin.site.urls)), )
默認的URLconf包含了一些被註釋起來的Django中經常使用的功能,僅僅只需去掉這些註釋就能夠開啓這些功能. 下面是URLconf中忽略被註釋的行後的實際內容
from django.conf.urls.defaults import * urlpatterns = patterns('', )
讓咱們逐行解釋一下代碼:
第一行導入django.conf.urls.defaults下的全部模塊,它們是Django URLconf的基本構造。 這包含了一個patterns函數。
第二行調用 patterns()
函數並將返回結果保存到 urlpatterns
變量。patterns函數當前只有一個參數—一個空的字符串。 (這個字符串能夠被用來表示一個視圖函數的通用前綴。具體咱們將在第八章裏面介紹。)
當前應該注意是 urlpatterns
變量, Django 指望能從 ROOT_URLCONF
模塊中找到它。 該變量定義了 URL 以及用於處理這些 URL 的代碼之間的映射關係。 默認狀況下,URLconf 全部內容都被註釋起來了——Django 應用程序仍是白版一塊。 (注:那是上一節中Django怎麼知道顯示歡迎頁面的緣由。 若是 URLconf 爲空,Django 會認定你才建立好新項目,所以也就顯示那種信息。
若是想在URLconf中加入URL和view,只需增長映射URL模式和view功能的Python tuple便可. 這裏演示如何添加view中hello功能.
from django.conf.urls.defaults import * from mysite.views import hello urlpatterns = patterns('', ('^hello/$', hello), )
請留意:爲了簡潔,咱們移除了註釋代碼。 若是你喜歡的話,你能夠保留那些行。)
咱們作了兩處修改。
首先,咱們從模塊 (在 Python 的 import 語法中, mysite/views.py
轉譯爲 mysite.views
) 中引入了 hello
視圖。 (這假設mysite/views.py在你的Python搜索路徑上。關於搜索路徑的解釋,請參照下文。)
接下來,咱們爲urlpatterns加上一行: (‘^hello/$’, hello), 這行被稱做URLpattern,它是一個Python的元組。元組中第一個元素是模式匹配字符串(正則表達式);第二個元素是那個模式將使用的視圖函數。
簡單來講,咱們只是告訴 Django,全部指向 URL /hello/
的請求都應由 hello
這個視圖函數來處理。
Python 搜索路徑
Python 搜索路徑 就是使用 import
語句時,Python 所查找的系統目錄清單。
舉例來講,假定你將 Python 路徑設置爲 ['','/usr/lib/python2.4/site-packages','/home/username/djcode/']
。若是執行代碼 from foo import bar
,Python 將會首先在當前目錄查找 foo.py
模塊( Python 路徑第一項的空字符串表示當前目錄)。 若是文件不存在,Python將查找 /usr/lib/python2.4/site-packages/foo.py
文件。
若是你想看Python搜索路徑的值,運行Python交互解釋器,而後輸入:
>>> import sys >>> print sys.path
一般,你沒必要關心 Python 搜索路徑的設置。 Python 和 Django 會在後臺自動幫你處理好。
討論一下URLpattern的語法是值得的,由於它不是顯而易見的。 雖然咱們想匹配地址/hello/,可是模式看上去與這有點差異。 這就是爲何:
Django在檢查URL模式前,移除每個申請的URL開頭的斜槓(/)。 這意味着咱們爲/hello/寫URL模式不用包含斜槓(/)。(剛開始,這樣可能看起來不直觀,但這樣的要求簡化了許多工做,如URL模式內嵌,咱們將在第八章談及。)
模式包含了一個尖號(^)和一個美圓符號($)。這些都是正則表達式符號,而且有特定的含義: 上箭頭要求表達式對字符串的頭部進行匹配,美圓符號則要求表達式對字符串的尾部進行匹配。
最好仍是用範例來講明一下這個概念。 若是咱們用尾部不是$的模式’^hello/’,那麼任何以/hello/開頭的URL將會匹配,例如:/hello/foo 和/hello/bar,而不只僅是/hello/。相似地,若是咱們忽略了尖號(^),即’hello/$’,那麼任何以hello/結尾的URL將會 匹配,例如:/foo/bar/hello/。若是咱們簡單使用hello/,即沒有^開頭和$結尾,那麼任何包含hello/的URL將會匹配,如: /foo/hello/bar。所以,咱們使用這兩個符號以確保只有/hello/匹配,很少也很多。
你大多數的URL模式會以^開始、以$結束,可是擁有複雜匹配的靈活性會更好。
你可能會問:若是有人申請訪問/hello(尾部沒有斜槓/)會怎樣。 由於咱們的URL模式要求尾部有一個斜槓(/),那個申請URL將不匹配。 然而,默認地,任何不匹配或尾部沒有斜槓(/)的申請URL,將被重定向至尾部包含斜槓的相同字眼的URL。 (這是受配置文件setting中APPEND_SLASH項控制的,參見附件D。)
若是你是喜歡全部URL都以’/’結尾的人(Django開發者的偏心),那麼你只須要在每一個URL後 添加斜槓,而且設置」APPEND_SLASH」爲」True」. 若是不喜歡URL以斜槓結尾或者根據每一個URL來決定,那麼須要設置」APPEND_SLASH」爲」False」,而且根據你本身的意願來添加結尾斜槓 /在URL模式後.
另外須要注意的是,咱們把hello視圖函數做爲一個對象傳遞,而不是調用它。 這是 Python (及其它動態語言的) 的一個重要特性: 函數是一級對象(first-class objects), 也就是說你能夠像傳遞其它變量同樣傳遞它們。 很酷吧?
啓動Django開發服務器來測試修改好的 URLconf, 運行命令行 python manage.py runserver
。 (若是你讓它一直運行也能夠,開發服務器會自動監測代碼改動並自動從新載入,因此不須要手工重啓) 開發服務器的地址是 http://127.0.0.1:8000/ ,打開你的瀏覽器訪問 http://127.0.0.1:8000/hello/ 。 你就能夠看到輸出結果了。 開發服務器將自動檢測Python代碼的更改來作必要的從新加載, 因此你不須要重啓Server在代碼更改以後。服務器運行地址`` http://127.0.0.1:8000/`` ,因此打開瀏覽器直接輸入`` http://127.0.0.1:8000/hello/`` ,你將看到由你的Django視圖輸出的Hello world。
萬歲! 你已經建立了第一個Django的web頁面。
正則表達式
正則表達式 (或 regexes ) 是通用的文本模式匹配的方法。 Django URLconfs 容許你 使用任意的正則表達式來作強有力的URL映射,不過一般你實際上可能只須要使用不多的一 部分功能。 這裏是一些基本的語法。
符號 | 匹配 |
---|---|
. (dot) |
任意單一字符 |
\d |
任意一位數字 |
[A-Z] |
A 到 Z 中任意一個字符(大寫) |
[a-z] |
a 到 z 中任意一個字符(小寫) |
[A-Za-z] |
a 到 z 中任意一個字符(不區分大小寫) |
+ |
匹配一個或更多 (例如, \d+ 匹配一個或 多個數字字符) |
[^/]+ |
一個或多個不爲‘/’的字符 |
* |
零個或一個以前的表達式(例如:\d? 匹配零個或一個數字) |
* |
匹配0個或更多 (例如, \d* 匹配0個 或更多數字字符) |
{1,3} |
介於一個和三個(包含)以前的表達式(例如,\d{1,3} 匹配一個或兩個或三個數字) |
有關正則表達式的更多內容,請訪問 http://www.djangoproject.com/r/python/re-module/.
目前,咱們的URLconf只定義了一個單獨的URL模式: 處理URL /hello/
。 當請求其餘URL會怎麼樣呢?
讓咱們試試看,運行Django開發服務器並訪問相似 http://127.0.0.1:8000/goodbye/
或者 http://127.0.0.1:8000/hello/subdirectory/
,甚至 http://127.0.0.1:8000/
(網站根目錄)。 你將會看到一個 「Page not found」 頁面(圖 3-2)。 由於你的URL申請在URLconf中沒有定義,因此Django顯示這條信息。
圖3-1: Django的404 Error頁
這個頁面比原始的404錯誤信息更加實用。 它同時精確的告訴你Django調用哪一個URLconf及其包含的每一個模式。 這樣,你應該能瞭解到爲何這個請求會拋出404錯誤。
固然,這些敏感的信息應該只呈現給你-開發者。 若是是部署到了因特網上的站點就不該該暴露 這些信息。 出於這個考慮,這個「Page not found」頁面只會在 調試模式(debug mode) 下 顯示。 咱們將在之後說明怎麼關閉調試模式。
在最後一節,若是你想經過http://127.0.0.1:8000/看網站根目錄你將看到一個404錯誤消息。Django不會增長任何東西在網站根目錄,在任何狀況下這個URL都不是特殊的 就像在URLconf中的其餘條目同樣,它也依賴於指定給它的URL模式.
儘管匹配網站根目錄的URL模式不能想象,可是仍是值得提一下的. 當爲網站根目錄實現一個視圖,你須要使用URL模式`` ‘^$’`` , 它表明一個空字符串。 例如:
from mysite.views import hello, my_homepage_view urlpatterns = patterns('', ('^$', my_homepage_view), # ... )
在繼續咱們的第二個視圖功能以前,讓咱們暫停一下去了解更多一些有關Django是怎麼工做的知識. 具體地說,當你經過在瀏覽器裏敲http://127.0.0.1:8000/hello/來訪問Hello world消息得時候,Django在後臺有些什麼動做呢?
全部均開始於setting文件。當你運行python manage.py runserver,腳本將在於manage.py同一個目錄下查找名爲setting.py的文件。這個文件包含了全部有關這個Django項目的配置信息,均大寫: TEMPLATE_DIRS , DATABASE_NAME , 等. 最重要的設置時ROOT_URLCONF,它將做爲URLconf告訴Django在這個站點中那些Python的模塊將被用到
還記得何時django-admin.py startproject建立文件settings.py和urls.py嗎?自動建立的settings.py包含一個ROOT_URLCONF配置用 來指向自動產生的urls.py. 打開文件settings.py你將看到以下:
ROOT_URLCONF = 'mysite.urls'
相對應的文件是mysite/urls.py
當訪問 URL /hello/
時,Django 根據 ROOT_URLCONF
的設置裝載 URLconf 。 而後按順序逐個匹配URLconf裏的URLpatterns,直到找到一個匹配的。 當找到這個匹配 的URLpatterns就調用相關聯的view函數,並把 HttpRequest
對象做爲第一個參數。 (稍後再給出 HttpRequest
的更多信息) (咱們將在後面看到HttpRequest的標準)
正如咱們在第一個視圖例子裏面看到的,一個視圖功能必須返回一個HttpResponse。 一旦作完,Django將完成剩餘的轉換Python的對象到一個合適的帶有HTTP頭和body的Web Response,(例如,網頁內容)。
總結一下:
進來的請求轉入/hello/.
Django經過在ROOT_URLCONF配置來決定根URLconf.
Django在URLconf中的全部URL模式中,查找第一個匹配/hello/的條目。
若是找到匹配,將調用相應的視圖函數
視圖函數返回一個HttpResponse
Django轉換HttpResponse爲一個適合的HTTP response, 以Web page顯示出來
你如今知道了怎麼作一個 Django-powered 頁面了,真的很簡單,只須要寫視圖函數並用 URLconfs把它們和URLs對應起來。 你可能會認爲用一系列正則表達式將URLs映射到函數也許會比較慢,但事實卻會讓你驚訝。
咱們的Hello world視圖是用來演示基本的Django是如何工做的,可是它不是一個動態網頁的例子,由於網頁的內容一直是同樣的. 每次去查看/hello/,你將會看到相同的內容,它相似一個靜態HTML文件。
咱們的第二個視圖,將更多的放些動態的東西例如當前日期和時間顯示在網頁上 這將很是好,簡單的下一步,由於它不引入了數據庫或者任何用戶的輸入,僅僅是輸出顯示你的服務器的內部時鐘. 它僅僅有限度的比Helloworld刺激一些,可是它將演示一些新的概念
這個視圖須要作兩件事情: 計算當前日期和時間,並返回包含這些值的HttpResponse 若是你對python頗有經驗,那確定知道在python中須要利用datetime模塊去計算時間 下面演示如何去使用它:
>>> import datetime >>> now = datetime.datetime.now() >>> now datetime.datetime(2008, 12, 13, 14, 9, 39, 2731) >>> print now 2008-12-13 14:09:39.002731
以上代碼很簡單,並無涉及Django。 它僅僅是Python代碼。 須要強調的是,你應該意識到哪些是純Python代碼,哪些是Django特性代碼。 (見上) 由於你學習了Django,但願你能將Django的知識應用在那些不必定須要使用Django的項目上。
爲了讓Django視圖顯示當前日期和時間,咱們僅須要把語句:datetime.datetime.now()放入視圖函數,而後返回一個HttpResponse對象便可。代碼以下:
from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
正如咱們的hello函數同樣,這個函數也保存在view.py中。爲了簡潔,上面咱們隱藏了hello函數。下面是完整的view.py文件內容:
from django.http import HttpResponse import datetime def hello(request): return HttpResponse("Hello world") def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
(從如今開始,如非必要,本文再也不重複列出先前的代碼。 你應該懂得識別哪些是新代碼,哪些是先前的。) (見上)
讓咱們分析一下改動後的views.py:
在文件頂端,咱們添加了一條語句:import datetime。這樣就能夠計算日期了。
函數中的第一行代碼計算當前日期和時間,並以
datetime.datetime
對象的形式保存爲局部變量now
。函數的第二行代碼用 Python 的格式化字符串(format-string)功能構造了一段 HTML 響應。 字符串中的%s是佔位符,字符串後面的百分號表示用它後面的變量now的值來代替%s。變量%s是一個datetime.datetime對象。它雖然不 是一個字符串,可是%s(格式化字符串)會把它轉換成字符串,如:2008-12-13 14:09:39.002731。這將致使HTML的輸出字符串爲:It is now 2008-12-13 14:09:39.002731。
(目前HTML是有錯誤的,但咱們這樣作是爲了保持例子的簡短。)
最後,正如咱們剛纔寫的hello函數同樣,視圖返回一個HttpResponse對象,它包含生成的響應。
添加上述代碼以後,還要在urls.py中添加URL模式,以告訴Django由哪個URL來處理這個視圖。 用/time/之類的字眼易於理解:
from django.conf.urls.defaults import * from mysite.views import hello, current_datetime urlpatterns = patterns('', ('^hello/$', hello), ('^time/$', current_datetime), )
這裏,咱們修改了兩個地方。 首先,在頂部導入current_datetime函數; 其次,也是比較重要的:添加URL模式來映射URL中的/time/和新視圖。 理解了麼?
寫好視圖而且更新URLconf以後,運行命令python manage.py runserver以啓動服務,在瀏覽器中輸入http://127.0.0.1:8000/time/。 你將看到當前的日期和時間。
Django時區
視乎你的機器,顯示的日期與時間可能和實際的相差幾個小時。 這是由於Django是有時區意識的,而且默認時區爲America/Chicago。 (它必須有個值,它的默認值是Django的誕生地:美國/芝加哥)若是你處在別的時區,你須要在settings.py文件中更改這個值。請參見它裏面 的註釋,以得到最新世界時區列表。
如今是好時機來指出Django和URL配置背後的哲學: 鬆耦合 原則。 簡單的說,鬆耦合是一個 重要的保證互換性的軟件開發方法。
Django的URL配置就是一個很好的例子。 在Django的應用程序中,URL的定義和視圖函數之間是鬆 耦合的,換句話說,決定URL返回哪一個視圖函數和實現這個視圖函數是在兩個不一樣的地方。 這使得 開發人員能夠修改一塊而不會影響另外一塊。
例如,考慮一下current_datetime視圖。 若是咱們想把它的URL 從原來的 /time/
改變到 /currenttime/
,咱們只須要快速的修改一下URL配置便可, 不用擔憂這個函數的內部實現。 一樣的,若是咱們想要修改這個函數的內部實現也不用擔憂會影響 到對應的URL。
此外,若是咱們想要輸出這個函數到 一些 URL, 咱們只須要修改URL配置而不用 去改動視圖的代碼。 在這個例子裏,current_datetime被兩個URL使用。 這是一個故弄玄虛的例子,但這個方法早晚會用得上。
urlpatterns = patterns('', ('^hello/$', hello), ('^time/$', current_datetime), ('^another-time-page/$', current_datetime), )
URLconf和視圖是鬆耦合的。 咱們將在本書中繼續給出這一重要哲學的相關例子。
在咱們的`` current_datetime`` 視圖範例中,儘管內容是動態的,可是URL ( /time/
)是靜態的。 在 大多數動態web應用程序,URL一般都包含有相關的參數。 舉個例子,一家在線書店會爲每一本書提供一個URL,如:/books/243/、/books/81196/。
讓咱們建立第三個視圖來顯示當前時間和加上時間誤差量的時間,設計是這樣的: /time/plus/1/
顯示當前時間+1個小時的頁面 /time/plus/2/
顯示當前時間+2個小時的頁面 /time/plus/3/
顯示當前時間+3個小時的頁面,以此類推。
新手可能會考慮寫不一樣的視圖函數來處理每一個時間誤差量,URL配置看起來就象這樣:
urlpatterns = patterns('', ('^time/$', current_datetime), ('^time/plus/1/$', one_hour_ahead), ('^time/plus/2/$', two_hours_ahead), ('^time/plus/3/$', three_hours_ahead), ('^time/plus/4/$', four_hours_ahead), )
很明顯,這樣處理是不太穩當的。 不但有不少冗餘的視圖函數,並且整個應用也被限制了只支持 預先定義好的時間段,2小時,3小時,或者4小時。 若是哪天咱們要實現 5 小時,咱們就 不得再也不單首創建新的視圖函數和配置URL,既重複又混亂。 咱們須要在這裏作一點抽象,提取 一些共同的東西出來。
關於漂亮URL的一點建議
若是你有其它web平臺的開發經驗(如PHP或Java),你可能會想:嘿!讓咱們用查詢字符串參數吧! 就像/time/plus?hours=3裏面的小時應該在查詢字符串中被參數hours指定(問號後面的是參數)。
你 能夠 在Django裏也這樣作 (若是你真的想要這樣作,咱們稍後會告訴你怎麼作), 可是Django的一個核心理念就是URL必須看起來漂亮。 URL /time/plus/3/
更加清晰, 更簡單,也更有可讀性,能夠很容易的大聲念出來,由於它是純文本,沒有查詢字符串那麼 複雜。 漂亮的URL就像是高質量的Web應用的一個標誌。
Django的URL配置系統可使你很容易的設置漂亮的URL,而儘可能不要考慮它的 反面 。
那麼,咱們如何設計程序來處理任意數量的時差? 答案是:使用通配符(wildcard URLpatterns)。正如咱們以前提到過,一個URL模式就是一個正則表達式。所以,這裏可使用d+來匹配1個以上的數字。
urlpatterns = patterns('', # ... (r'^time/plus/\d+/$', hours_ahead), # ... )
這裏使用# …來表示省略了其它可能存在的URL模式定義。 (見上)
這個URL模式將匹配相似 /time/plus/2/
, /time/plus/25/
,甚至 /time/plus/100000000000/
的任何URL。 更進一步,讓咱們把它限制在最大容許99個小時, 這樣咱們就只容許一個或兩個數字,正則表達式的語法就是 \d{1,2}
:
(r'^time/plus/\d{1,2}/$', hours_ahead),
備註
在建造Web應用的時候,儘量多考慮可能的數據輸入是很重要的,而後決定哪些咱們能夠接受。 在這裏咱們就設置了99個小時的時間段限制。
另一個重點,正則表達式字符串的開頭字母「r」。 它告訴Python這是個原始字符串,不須要處理裏面的反斜槓(轉義字符)。 在普通Python字符串中,反斜槓用於特殊字符的轉義。好比n轉義成一個換行符。 當你用r把它標示爲一個原始字符串後,Python再也不視其中的反斜槓爲轉義字符。也就是說,「n」是兩個字符串:「」和「n」。因爲反斜槓在 Python代碼和正則表達式中有衝突,所以建議你在Python定義正則表達式時都使用原始字符串。 從如今開始,本文全部URL模式都用原始字符串。
如今咱們已經設計了一個帶通配符的URL,咱們須要一個方法把它傳遞到視圖函數裏去,這樣 咱們只用一個視圖函數就能夠處理全部的時間段了。 咱們使用圓括號把參數在URL模式裏標識 出來。 在這個例子中,咱們想要把這些數字做爲參數,用圓括號把 \d{1,2}
包圍起來:
(r'^time/plus/(\d{1,2})/$', hours_ahead),
若是你熟悉正則表達式,那麼你應該已經瞭解,正則表達式也是用圓括號來從文本里 提取 數據的。
最終的URLconf包含上面兩個視圖,如:
from django.conf.urls.defaults import * from mysite.views import hello, current_datetime, hours_ahead urlpatterns = patterns('', (r'^hello/$', hello), (r'^time/$', current_datetime), (r'^time/plus/(\d{1,2})/$', hours_ahead), )
如今開始寫 hours_ahead
視圖。
編碼次序
這個例子中,咱們先寫了URLpattern ,而後是視圖,可是在前面的例子中, 咱們先寫了視圖,而後是URLpattern 。 哪種方式比較好?
嗯,怎麼說呢,每一個開發者是不同的。
若是你是喜歡從整體上來把握事物(注: 或譯爲「大局觀」)類型的人,你應該會想在項目開始 的時候就寫下全部的URL配置。
若是你從更像是一個自底向上的開發者,你可能更喜歡先寫視圖, 而後把它們掛接到URL上。 這一樣是能夠的。
最後,取決與你喜歡哪一種技術,兩種方法都是能夠的。 (見上)
hours_ahead
和咱們之前寫的 current_datetime
很象,關鍵的區別在於: 它多了一個額外參數,時間差。 如下是view代碼:
from django.http import Http404, HttpResponse import datetime def hours_ahead(request, offset): try: offset = int(offset) except ValueError: raise Http404() dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)
讓咱們逐行分析一下代碼:
視圖函數,
hours_ahead
, 有 兩個 參數:request
和offset
. (見上)
request
是一個HttpRequest
對象, 就像在current_datetime
中同樣. 再說一次好了: 每個視圖 老是 以一個HttpRequest
對象做爲 它的第一個參數。 (見上)
offset
是從匹配的URL裏提取出來的。 例如:若是請求URL是/time/plus/3/,那麼offset將會是3;若是請求URL是/time/plus/21/,那麼offset將會是 21。請注意:捕獲值永遠都是字符串(string)類型,而不會是整數(integer)類型,即便這個字符串全由數字構成(如:「21」)。(從技術上來講,捕獲值老是Unicode objects,而不是簡單的Python字節串,但目前不須要擔憂這些差異。)
在這裏咱們命名變量爲
offset
,你也能夠任意命名它,只要符合Python 的語法。 變量名是可有可無的,重要的是它的位置,它是這個函數的第二個 參數 (在request
的後面)。 你還可使用關鍵字來定義它,而不是用 位置。咱們在這個函數中要作的第一件事情就是在
offset
上調用int()
. 這會把這個字符串值轉換爲整數。請留意:若是你在一個不能轉換成整數類型的值上調用int(),Python將拋出一個 ValueError異常。如:int(‘foo’)。在這個例子中,若是咱們遇到ValueError異常,咱們將轉爲拋出 django.http.Http404異常——正如你想象的那樣:最終顯示404頁面(提示信息:頁面不存在)。
機靈的讀者可能會問: 咱們在URL模式中用正則表達式(d{1,2})約束它,僅接受數字怎麼樣?這樣不管如何,offset都是由數字構成的。 答案是:咱們不會這麼作,由於URLpattern提供的是「適度但有用」級別的輸入校驗。萬一這個視圖函數被其它方式調用,咱們仍需自行檢查 ValueError。 實踐證實,在實現視圖函數時,不臆測參數值的作法是比較好的。 鬆散耦合,還記得麼?
下一行,計算當前日期/時間,而後加上適當的小時數。 在current_datetime視圖中,咱們已經見過datetime.datetime.now()。這裏新的概念是執行日期/時間的算術操做。我 們須要建立一個datetime.timedelta對象和增長一個datetime.datetime對象。 結果保存在變量dt中。
這一行還說明了,咱們爲何在offset上調用int()——datetime.timedelta函數要求hours參數必須爲整數類型。
這行和前面的那行的的一個微小差異就是,它使用帶有兩個值的Python的格式化字符串功能, 而不只僅是一個值。 所以,在字符串中有兩個
%s
符號和一個以進行插入的值的元組:(offset, dt)
。最終,返回一個HTML的HttpResponse。 現在,這種方式已通過時了。
在完成視圖函數和URL配置編寫後,啓動Django開發服務器,用瀏覽器訪問 http://127.0.0.1:8000/time/plus/3/
來確認它工做正常。 而後是 http://127.0.0.1:8000/time/plus/5/
。再而後是 http://127.0.0.1:8000/time/plus/24/
。最後,訪問 http://127.0.0.1:8000/time/plus/100/
來檢驗URL配置裏設置的模式是否只 接受一個或兩個數字;Django會顯示一個 Page not found error 頁面, 和之前看到的 404 錯誤同樣。 訪問URL http://127.0.0.1:8000/time/plus/
(沒有 定義時間差) 也會拋出404錯誤。
花幾分鐘時間欣賞一下咱們寫好的Web應用程序,而後咱們再來搞點小破壞。 咱們故意在 views.py
文件中引入一項 Python 錯誤,註釋掉 hours_ahead
視圖中的 offset = int(offset)
一行。
def hours_ahead(request, offset): # try: # offset = int(offset) # except ValueError: # raise Http404() dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)
啓動開發服務器,而後訪問 /time/plus/3/
。你會看到一個包含大量信息的出錯頁,最上面 的一條 TypeError
信息是: "unsupported type for timedelta hours component: unicode"
.
怎麼回事呢? 是的, datetime.timedelta
函數要求 hours
參數必須爲整型, 而咱們註釋掉了將 offset
轉爲整型的代碼。 這樣致使 datetime.timedelta
彈出 TypeError
異常。
這個例子是爲了展現 Django 的出錯頁面。 咱們來花些時間看一看這個出錯頁,瞭解一下其中 給出了哪些信息。
如下是值得注意的一些要點:
在頁面頂部,你能夠獲得關鍵的異常信息: 異常數據類型、異常的參數 (如本例中的
"unsupported type"
)、在哪一個文件中引起了異常、出錯的行號等等。在關鍵異常信息下方,該頁面顯示了對該異常的完整 Python 追蹤信息。 這相似於你在 Python 命令行解釋器中得到的追溯信息,只不事後者更具交互性。 對棧中的每一幀,Django 均顯示了其文件名、函數或方法名、行號及該行源代碼。
點擊該行代碼 (以深灰色顯示),你能夠看到出錯行的先後幾行,從而得知相關上下文狀況。
點擊棧中的任何一幀的「Local vars」能夠看到一個全部局部變量的列表,以及在出錯 那一幀時它們的值。 這些調試信息至關有用。
注意「Traceback」下面的「Switch to copy-and-paste view」文字。 點擊這些字,追溯會 切換另外一個視圖,它讓你很容易地複製和粘貼這些內容。 當你想同其餘人分享這些異常 追溯以得到技術支持時(好比在 Django 的 IRC 聊天室或郵件列表中),可使用它。
你按一下下面的「Share this traceback on a public Web site」按鈕,它將會完成這項工做。 點擊它以傳回追溯信息至http://www.dpaste.com/,在那裏你能夠獲得一個單獨的URL並與其餘人分享你的追溯信息。
接下來的「Request information」部分包含了有關產生錯誤的 Web 請求的大量信息: GET 和 POST、cookie 值、元數據(象 CGI 頭)。 在附錄H裏給出了request的對象的 完整參考。
Request信息的下面,「Settings」列出了 Django 使用的具體配置信息。 (咱們已經說起過ROOT_URLCONF,接下來咱們將向你展現各式的Django設置。 附錄D覆蓋了全部可用的設置。)
Django 的出錯頁某些狀況下有能力顯示更多的信息,好比模板語法錯誤。 咱們討論 Django 模板系統時再說它們。 如今,取消 offset = int(offset)
這行的註釋,讓它從新正常 工做。
不知道你是否是那種使用當心放置的 print
語句來幫助調試的程序員? 你其實能夠用 Django 出錯頁來作這些,而不用 print
語句。 在你視圖的任何位置,臨時插入一個 assert False
來觸發出錯頁。 而後,你就能夠看到局部變量和程序語句了。 這裏有個使用hours_ahead視圖的例子:
def hours_ahead(request, offset): try: offset = int(offset) except ValueError: raise Http404() dt = datetime.datetime.now() + datetime.timedelta(hours=offset) assert False html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt) return HttpResponse(html)
最後,很顯然這些信息不少是敏感的,它暴露了你 Python 代碼的內部結構以及 Django 配置,在 Internet 上公開這信息是很愚蠢的。 不懷好意的人會嘗試使用它攻擊你的 Web 應用程序,作些下流之事。 所以,Django 出錯信息僅在 debug 模式下才會顯現。 咱們稍後 說明如何禁用 debug 模式。 如今,你只要知道 Django 服務器在你開啓它時默認運行在 debug 模式就好了。 (聽起來很熟悉? 頁面沒有發現錯誤,如前所述,工做正常。)