天天看點

Django 部落格開發教程 8 - 部落格文章詳情頁

首頁展示的是所有文章的清單,當使用者看到感興趣的文章時,他點選文章的标題或者繼續閱讀的按鈕,應該跳轉到文章的詳情頁面來閱讀文章的詳細内容。現在讓我們來開發部落格的詳情頁面,有了前面的基礎,開發流程都是一樣的了:首先配置 url,即把相關的 url 和視圖函數綁定在一起,然後實作視圖函數,編寫模闆并讓視圖函數渲染模闆。

回顧一下我們首頁視圖的 url,在 blogurls.py 檔案裡,我們寫了:

首頁視圖比對的 url 去掉域名後其實就是一個空的字元串。對文章詳情視圖而言,每篇文章對應着不同的 url。比如我們可以把文章詳情頁面對應的視圖設計成這個樣子:當使用者通路 <網站域名>/post/1/ 時,顯示的是第一篇文章的内容,而當使用者通路 <網站域名>/post/2/ 時,顯示的是第二篇文章的内容,這裡數字代表了第幾篇文章,也就是資料庫中 post 記錄的 id 值。下面依照這個規則來綁定 url 和視圖:

django 使用正規表達式來比對使用者通路的網址。這裡 <code>r'^post/(?p&lt;pk&gt;[0-9]+)/$'</code> 整個正規表達式剛好比對我們上面定義的 url 規則。這條正規表達式的含義是,以 post/ 開頭,後跟一個至少一位數的數字,并且以 / 符号結尾,如 post/1/、 post/255/ 等都是符合規則的,[0-9]+ 表示一位或者多位數。此外這裡 <code>(?p&lt;pk&gt;[0-9]+)</code> 表示命名捕獲組,其作用是從使用者通路的 url 裡把括号内比對的字元串捕獲并作為關鍵字參數傳給其對應的視圖函數 <code>detail</code>。比如當使用者通路 post/255/ 時(注意 django 并不關心域名,而隻關心去掉域名後的相對 url),被括起來的部分 <code>(?p&lt;pk&gt;[0-9]+)</code> 比對 255,那麼這個 255 會在調用視圖函數 detail 時被傳遞進去,實際上視圖函數的調用就是這個樣子:<code>detail(request, pk=255)</code>。我們這裡必須從 url 裡捕獲文章的 id,因為隻有這樣我們才能知道使用者通路的究竟是哪篇文章。

可能上述的正規表達式你有點難以了解,關于正規表達式的部分并非 django 相關的内容,而是 python 的内容。django 隻是在這裡使用了 python 處理正規表達式的 re 子產品。是以如果想更好地了解 python 中正規表達式的相關知識,請自行檢視 python 官方文檔中 re 子產品的文檔。

此外我們通過 <code>app_name='blog'</code> 告訴 django 這個 urls.py 子產品是屬于 blog 應用的,這種技術叫做視圖函數命名空間。我們看到 blogurls.py 目前有兩個視圖函數,并且通過 name 屬性給這些視圖函數取了個别名,分别是 index、detail。但是一個複雜的 django 項目可能不止這些視圖函數,例如一些第三方應用中也可能有叫 index、detail 的視圖函數,那麼怎麼把它們區分開來,防止沖突呢?方法就是通過 app_name 來指定命名空間,命名空間具體如何使用将在下面介紹。如果你忘了在 blogurls.py 中添加這一句,接下來你可能會得到一個 nomatchreversed 異常。

為了友善地生成上述的 url,我們在 <code>post</code> 類裡定義一個 <code>get_absolute_url</code> 方法,注意 <code>post</code> 本身是一個 python 類,在類中我們是可以定義任何方法的。

注意到 url 配置中的 <code>url(r'^post/(?p&lt;pk&gt;[0-9]+)/$', views.detail, name='detail')</code> ,我們設定的 <code>name='detail'</code> 在這裡派上了用場。看到這個 <code>reverse</code> 函數,它的第一個參數的值是 <code>'blog:detail'</code>,意思是 blog 應用下的 <code>name=detail</code> 的函數,由于我們在上面通過 <code>app_name = 'blog'</code> 告訴了 django 這個 url 子產品是屬于 blog 應用的,是以 django 能夠順利地找到 blog 應用下 name 為 detail 的視圖函數,于是 <code>reverse</code> 函數會去解析這個視圖函數對應的 url,我們這裡 detail 對應的規則就是 <code>post/(?p&lt;pk&gt;[0-9]+)/</code> 這個正規表達式,而正規表達式部分會被後面傳入的參數 <code>pk</code> 替換,是以,如果 <code>post</code> 的 id(或者 pk,這裡 pk 和 id 是等價的) 是 255 的話,那麼 <code>get_absolute_url</code> 函數傳回的就是 /post/255/ ,這樣 post 自己就生成了自己的 url。

接下來就是實作我們的 <code>detail</code> 視圖函數了:

視圖函數很簡單,它根據我們從 url 捕獲的文章 id(也就是 pk,這裡 pk 和 id 是等價的)擷取資料庫中文章 id 為該值的記錄,然後傳遞給模闆。注意這裡我們用到了從 django.shortcuts 子產品導入的 <code>get_object_or_404</code> 方法,其作用就是當傳入的 pk 對應的 post 在資料庫存在時,就傳回對應的 <code>post</code>,如果不存在,就給使用者傳回一個 404 錯誤,表明使用者請求的文章不存在。

在 index 頁面部落格文章清單的标題和繼續閱讀按鈕寫上超連結跳轉的連結,即文章 <code>post</code> 對應的詳情頁的 url,讓使用者點選後可以跳轉到 detail 頁面:

這裡我們修改兩個地方,第一個是文章标題處:

我們把 a 标簽的 href 屬性的值改成了 {{ post.get_absolute_url }}。回顧一下模闆變量的用法,由于 get_absolute_url 這個方法(我們定義在 post 類中的)傳回的是 <code>post</code> 對應的 url,是以這裡 {{ post.get_absolute_url }} 最終會被替換成該 <code>post</code> 自身的 url。

同樣,第二處修改的是繼續閱讀按鈕的連結:

我們看到 index.html 檔案和 detail.html 檔案除了 main 标簽包裹的部分不同外,其它地方都是相同的,我們可以把相同的部分抽取出來,放到 base.html 裡。首先在 templates 目錄下建立一個 base.html 檔案,這時候你的項目目錄應該變成了這個樣子:

把 index.html 的内容全部拷貝到 base.html 檔案裡,然後删掉 main 标簽包裹的内容,替換成如下的内容。

這裡 block 也是一個模闆标簽,其作用是占位。比如這裡的 {% block main %}{% endblock main %} 是一個占位框,main 是我們給這個 block 取的名字。下面我們會看到 block 标簽的作用。同時我們也在 aside 标簽下加了一個 {% block toc %}{% endblock toc %} 占位框,因為 detail.html 中 aside 标簽下會多一個目錄欄。當 {% block toc %}{% endblock toc %} 中沒有任何内容時,{% block toc %}{% endblock toc %} 在模闆中不會顯示。但當其中有内容是,模闆就會顯示 block 中的内容。

在 index.html 裡,我們在檔案最頂部使用 <code>{% extends 'base.html' %}</code> 繼承 base.html,這樣就把 base.html 裡的代碼繼承了過來,另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 頁面應該顯示的内容:

這樣 base.html 裡的代碼加上 {% block main %}{% endblock main %} 裡的代碼就和最開始 index.html 裡的代碼一樣了。這就是模闆繼承的作用,公共部分的代碼放在 base.html 裡,而其它頁面不同的部分通過替換 {% block main %}{% endblock main %} 占位标簽裡的内容即可。

如果你對這種模闆繼承還是有點糊塗,可以把這種繼承和 python 中類的繼承類比。base.html 就是父類,index.html 就是子類。index.html 繼承了 base.html 中的全部内容,同時它自身還有一些内容,這些内容就通過 “覆寫” {% block main %}{% endblock main %}(把 block 看做是父類的屬性)的内容添加即可。

detail 頁面處理起來就簡單了,同樣繼承 base.html ,在 {% block main %}{% endblock main %} 裡填充 detail.html 頁面應該顯示的内容,以及在 {% block toc %}{% endblock toc %} 中填寫 base.html 中沒有的目錄部分的内容。不過目前的目錄隻是占位資料,我們在以後會實作如何從文章中自動摘取目錄。

修改 article 标簽下的一些内容,讓其顯示文章的實際資料:

再次從首頁點選一篇文章的标題或者繼續閱讀按鈕跳轉到詳情頁面,可以看到預期效果了!

Django 部落格開發教程 8 - 部落格文章詳情頁

如果遇到問題,請通過下面的方式尋求幫助。

将問題的較長的描述通過郵件發送到 [email protected],一般會在 24 小時内回複。