9. 博客文章詳情頁

本教程內(nèi)容已過時抄伍,更新版教程請訪問: Django 博客開發(fā)入門教程卧波。

這是 Django 博客教程的第 9 篇风题,在閱讀此篇教程以前,請確保你已閱讀 Django 博客教程的前 8 篇:
1. Django 博客教程:前言
2. 搭建開發(fā)環(huán)境
3. 建立我們的 django 博客應(yīng)用
4. 創(chuàng)建 django 博客的數(shù)據(jù)庫模型
5. 讓 django 完成翻譯——遷移數(shù)據(jù)庫模型
6. django 博客首頁視圖
7. 真正的 django 博客首頁視圖
8. 在 django admin 后臺發(fā)布我們的文章

首頁展示的是所有文章的列表稽亏,當(dāng)用戶看到感興趣的文章時,他點擊文章的標(biāo)題或者繼續(xù)閱讀的按鈕缕题,應(yīng)該跳轉(zhuǎn)到文章的詳情頁面來閱讀文章的詳細(xì)內(nèi)容截歉。本節(jié)我們來開發(fā)博客的詳情頁面,有了前面的基礎(chǔ)烟零,套路都是一樣的了:首先把相關(guān)的 url 和視圖函數(shù)綁定在一起瘪松,然后實現(xiàn)視圖函數(shù)咸作,編寫響應(yīng)的模板讓視圖函數(shù)渲染模板。

回顧一下我們首頁視圖的 url宵睦,在 blog/urls.py 文件里记罚,我們寫了:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
]

首頁視圖匹配的 url 去掉域名后其實就是一個空的字符串。對文章詳情視圖而言状飞,每篇文章對應(yīng)著不同的 url毫胜。比如我們可以把文章詳情頁面對應(yīng)的視圖設(shè)計成這個樣子:當(dāng)用戶訪問 <網(wǎng)站域名>/post/1/ 時,顯示的是第一篇文章的內(nèi)容诬辈,而當(dāng)用戶訪問 <網(wǎng)站域名>/post/2/ 時酵使,顯示的是第二篇文章的內(nèi)容,這里數(shù)字代表了第幾篇文章焙糟,也就是數(shù)據(jù)庫中 Post 記錄的 id口渔。下面依照這個規(guī)則來綁定 url 和視圖:

from django.conf.urls import url

from . import views

app_name = 'blog'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),
]

django 使用正則表達(dá)式來匹配對應(yīng)的域名。這里 r'^post/[0-9]+/$' 整個正則表達(dá)式剛好匹配我們上面定義的 url 規(guī)則穿撮。這條正則表達(dá)式的含義是缺脉,以 post/ 開頭,后跟一個至少一位數(shù)的數(shù)字悦穿,并且以 / 符號結(jié)尾攻礼,如 /post/1/、 /post/255/ 等都是符合規(guī)則的栗柒,[0-9]+ 表示數(shù)字 0 到 9 重復(fù)至少一次的數(shù)字礁扮。此外我們這里 (?P<pk>[0-9]+) 表示命名捕獲組,其作用是從用戶訪問的 url 里把括號內(nèi)匹配的字符串捕獲并作為關(guān)鍵字參數(shù)傳給視圖函數(shù) detail瞬沦。比如當(dāng)用戶訪問 /post/255/ 時(注意 django 并不關(guān)心域名太伊,而只關(guān)心去掉域名后的相對 url),被括起來的部分 (?P<pk>[0-9]+) 匹配 255逛钻,那么這個 255 會在調(diào)用視圖函數(shù) detail 時被傳遞進(jìn)去僚焦,實際上視圖函數(shù)的調(diào)用就是這個樣子:detail(request, pk=255)。我們這里必須從 url 里捕獲文章的 id曙痘,因為只有這樣我們才能知道用戶訪問的是那篇文章芳悲。

此外我們通過 app_name='blog' 告訴 django 這個 urls.py 模塊是屬于 blog 應(yīng)用的,其具體作用會在下面介紹屡江。

為了方便地生成上述的 url芭概,我們在 Post 模型里定義一個 get_absolute_url 方法,注意 Post 本身是一個 Python 類惩嘉,在類中我們是可以定義任何方法的罢洲。

blog/models.py

from django.db import models
from django.utils.six import python_2_unicode_compatible

# 導(dǎo)入 reverse 函數(shù)
from django.urls import reverse

@python_2_unicode_compatible
class Category(models.Model):
    ...


@python_2_unicode_compatible
class Tag(models.Model):
    ...


@python_2_unicode_compatible
class Post(models.Model):
    ...

    def __str__(self):
        return self.title
    
    # 自定義 get_absolute_url 方法
    def get_absolute_url(self):
        return reverse('blog:detail', kwargs={'pk': self.pk})

注意到 URL 配置中 url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail') ,我們設(shè)定、的 name='detail'惹苗,這里派上了用場殿较。看到這個 reverse 函數(shù)桩蓉,它的第一個參數(shù)的值是 'blog:detail'淋纲,意思是 blog 應(yīng)用下的 name=detail 函數(shù),由于我們在上面通過 app_name = 'blog' 告訴了 django 這個 URL 模塊是屬于 blog 應(yīng)用的院究,因此 django 能夠順利地找到 blog 應(yīng)用下 name 為 detail 的視圖函數(shù)洽瞬,于是 django 會去解析這個視圖函數(shù)對應(yīng)的 url,我們這里 detail 對應(yīng)的規(guī)則就是 post/(?P<pk>[0-9]+)/ 這個正則表達(dá)式規(guī)則业汰,而正則表達(dá)式部分會被后面?zhèn)魅氲膮?shù) pk 替換伙窃,所以,如果 post 的 id 是 255 的話样漆,那么 get_absolute_url 函數(shù)返回的就是 /post/255/ 为障,這樣 Post 自己就生成了自己的 url。

接下來就是實現(xiàn)我們的 detail 視圖函數(shù)了:

blog/views.py

from django.shortcuts import render, get_object_or_404

from .models import Post

def index(request):
    # ...

def detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/detail.html', context={'post': post})

視圖函數(shù)很簡單放祟,它根據(jù)我們從 url 捕獲的文章 id(也就是 pk鳍怨,pk 意為主鍵,這里的主鍵就是 id)獲取我們的 post跪妥,然后傳遞給模板鞋喇。注意這里我們用到了從 django.shortcuts 模塊導(dǎo)入的 get_object_or_404 方法,其作用就是當(dāng)傳入的 pk 對應(yīng)的 Post 在數(shù)據(jù)庫存在時眉撵,就返回找到的 post确徙,如果不存在,就給用戶返回一個 404 錯誤执桌,表明用戶請求的文章不存在。

接下來就是書寫模板文件芜赌,從文件夾中把 single.html 拷貝到 template/blog 的目錄下(和 index.html 在同一級目錄)仰挣,然后改名為 detail.html。在 index 頁面博客文章列表的標(biāo)題和繼續(xù)閱讀按鈕填上鏈接缠沈,讓用戶點擊后可以跳轉(zhuǎn)到 detail 頁面:

templates/index.html

<article class="post post-1">
  <header class="entry-header">
    <h1 class="entry-title">
      <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
    </h1>
    ...
  </header>
  <div class="entry-content clearfix">
    ...
    <div class="read-more cl-effect-14">
      <a href="{{ post.get_absolute_url }}" class="more-link">繼續(xù)閱讀 <span class="meta-nav">→</span></a>
    </div>
  </div>
</article>
{% empty %}
<div class="no-post">暫時還沒有發(fā)布的文章膘壶!</div>
{% endfor %}

這里我們修改兩個地方,第一個是文章標(biāo)題處:

<h1 class="entry-title">
  <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h1>

我們把 a 標(biāo)簽的 href 的值改成了 {{ post.get_absolute_url }}洲愤,回顧一下模板變量的用法颓芭,由于 get_absolute_url 這個方法(我們定義在 Post 類中的)返回的是 post 對應(yīng)的 url,因此這里 {{ post.get_absolute_url }} 最終會被替換成該 post 自身的 url柬赐。

同樣亡问,第二處修改的是繼續(xù)閱讀按鈕的鏈接:

<a href="{{ post.get_absolute_url }}" class="more-link">繼續(xù)閱讀 <span class="meta-nav">→</span>
</a>

這樣當(dāng)我們點擊首頁文章的標(biāo)題或者繼續(xù)閱讀按鈕后就會跳轉(zhuǎn)到該篇文章對應(yīng)的詳情頁面了。然而如果你嘗試跳轉(zhuǎn)到詳情頁后,你會發(fā)現(xiàn)樣式是亂的州藕。這在[寫首頁視圖函數(shù)][]時講過束世,由于我們直接復(fù)制的模板,還沒有正確地處理靜態(tài)文件床玻。我們可以按照那一節(jié)的方法修改靜態(tài)文件的引入路徑毁涉,但是很麻煩。你會發(fā)現(xiàn)在任何頁面都是需要引入這些靜態(tài)文件的锈死,如果每個頁面都要修改會很麻煩贫堰,而且代碼都是重復(fù)的。下面就介紹 django 模板繼承的方法來幫我們消除這些重復(fù)操作待牵。

我們看到 index.html 文件和 detail.html 文件除了 main 包裹的部分不同外其屏,其他地方都是相同的,我們可以把相同的部分抽取出來洲敢,放到 base.html 里漫玄。首先在 templates 目錄下新建一個 base.html 文件,把 index.html 的內(nèi)容全部拷貝過來压彭,然后刪掉 main 標(biāo)簽包裹的內(nèi)容睦优,替換成如下的內(nèi)容。

templates/base.html

...
<main class="col-md-8">
    {% block main %}
    {% endblock main %}
</main>
...

這里 {% block main %}{% endblock main %} 是一個占位框壮不,下面我們會看到它的作用汗盘。、

在 index.html 里询一,我們使用 {% extends 'base.html' %} 繼承 base.html隐孽,這樣就把 base.html 里的代碼繼承了過來,另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 頁面應(yīng)該顯示的內(nèi)容:

{% extends 'base.html' %}

{% block main %}
    {% for post in post_list %}
        <article class="post post-1">
          ...
        </article>
    {% empty %}
        <div class="no-post">暫時還沒有發(fā)布的文章健蕊!</div>
    {% endfor %}
{% endblock main %}

這樣 base.html 里的代碼加上 {% block main %}{% endblock main %} 里的代碼就和最開始 index.html 里的代碼一樣了菱阵。這就是模板繼承的作用,公共部分的代碼放在 base.html 里缩功,不同部分的代碼通過替換 {% block main %}{% endblock main %} 占位框里的內(nèi)容即可晴及。

detail 頁面處理起來就簡單了,同樣繼承 base.html 嫡锌,在 {% block main %}{% endblock main %} 里填充 detail.html 頁面應(yīng)該顯示的內(nèi)容虑稼。

blog/detail.html

{% extends 'base.html' %}

{% block main %}
    <article class="post post-1">
      ...
    </article>
{% endblock main %}

修改 article 標(biāo)簽下的一些內(nèi)容,讓其顯示文章的實際數(shù)據(jù)势木。

<header class="entry-header">
  <h1 class="entry-title">{{ post.title }}</h1>
  <div class="entry-meta">
    <span class="post-category"><a href="#">{{ post.category.name }} </a></span>

    <span class="post-date">
      <a href="#">
        <time class="entry-date" datetime="{{ post.created_time }}">{{ post.created_time }} </time>
      </a>
    </span>

    <span class="post-author"><a href="#">{{ post.author }} </a></span>

    <span class="comments-link"><a href="#">4 Comments</a></span>
  </div>
</header>
<div class="entry-content clearfix">
  {{ post.body }}
</div>

再次從首頁點擊一篇文章的標(biāo)題跳轉(zhuǎn)到詳情頁面蛛倦,可以看到預(yù)期效果了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啦桌,一起剝皮案震驚了整個濱河市溯壶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖茸塞,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躲庄,死亡現(xiàn)場離奇詭異,居然都是意外死亡钾虐,警方通過查閱死者的電腦和手機(jī)噪窘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來效扫,“玉大人倔监,你說我怎么就攤上這事【剩” “怎么了浩习?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長济丘。 經(jīng)常有香客問我谱秽,道長,這世上最難降的妖魔是什么摹迷? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任疟赊,我火速辦了婚禮,結(jié)果婚禮上峡碉,老公的妹妹穿的比我還像新娘近哟。我一直安慰自己,他們只是感情好鲫寄,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布吉执。 她就那樣靜靜地躺著,像睡著了一般地来。 火紅的嫁衣襯著肌膚如雪戳玫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天未斑,我揣著相機(jī)與錄音量九,去河邊找鬼。 笑死颂碧,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的类浪。 我是一名探鬼主播载城,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼费就!你這毒婦竟也來了诉瓦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎睬澡,沒想到半個月后固额,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡煞聪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年斗躏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昔脯。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡啄糙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出云稚,到底是詐尸還是另有隱情隧饼,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布静陈,位于F島的核電站燕雁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鲸拥。R本人自食惡果不足惜拐格,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崩泡。 院中可真熱鬧禁荒,春花似錦、人聲如沸角撞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谒所。三九已至热康,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間劣领,已是汗流浹背姐军。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留尖淘,地道東北人奕锌。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像村生,于是被迫代替她去往敵國和親惊暴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容