Django 學習小組:博客開發(fā)實戰(zhàn)第五周——基于類的通用視圖詳解(一)

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

通過三周的時間我們開發(fā)了一個簡單的個人 Blog丁寄,相關教程:

第一周Django 學習小組:博客開發(fā)實戰(zhàn)第一周教程 —— 編寫博客的 Model 和首頁面
第二周Django 學習小組:博客開發(fā)實戰(zhàn)第二周教程 —— 博客詳情頁面和分類頁面
第三周Django 學習小組:博客開發(fā)實戰(zhàn)第三周教程——文章列表分頁和代碼語法高亮
第四周Django 學習小組:博客開發(fā)實戰(zhàn)第四周——標簽云與文章歸檔
第六周Django 學習小組:博客開發(fā)實戰(zhàn)第六周教程 —— 實現(xiàn)評論功能

有朋友反應說對于 Django 的 class-based-view(基于類的通用視圖)還有很多不明白的地方,因此接下來我們會出一系列文章講解幾個常用的基于類的視圖的用法,并在適當?shù)脑创a層面下講解其機理和如何按照我們的需要拓展它伊磺。

本教程首先介紹兩個 Blog 項目中遇到的通用視圖:ListViewDetailView盛正。從名字我們可以對其功能略窺一二,ListView 用于 List(列出)一系列 Model (比如文章列表)屑埋,DetailView 獲取某個 Model(比如某篇文章)以展示其細節(jié)豪筝。

提示:在閱讀教程的過程中,如有任何問題請訪問我們項目的 GithHub 或評論留言以獲取幫助摘能,本教程的相關代碼已全部上傳在 GitHub 的 blog-tutorial 分支 上壤蚜,請點擊鏈接獲取。如果你對我們的教程或者項目有任何改進建議徊哑,請您隨時告知我們袜刷。更多交流請加入我們的郵件列表 django_study@groups.163.com 和關注我們在 GithHub 上的項目。

本文首發(fā)于編程派微信公眾號:編程派(微信號:codingpy)是一個專注Python編程的公眾號莺丑,每天更新有關Python的國外教程和優(yōu)質(zhì)書籍等精選干貨著蟹,歡迎關注。

ListView

在開發(fā)一個網(wǎng)站時梢莽,我們常常需要獲取存儲在數(shù)據(jù)庫中的某個 Model 的列表萧豆,比如 Blog 要獲取文章(Article)列表以顯示到首頁,通常我們都會寫如下的視圖函數(shù)來滿足我們的需求:

def index(request):
    """
    獲取 Article 列表以渲染首頁模板
    """
    article_list = Article.objects.all() # 獲取文章列表
    category_list = Category.objects.all() # 獲取分類列表
    context = { 'article_list' : article_list , 'category_list' : category_list }
    return render_to_response('blog/index.html',context)

當然這僅僅是一個最為基本的視圖函數(shù)的例子昏名,Django 開發(fā)者發(fā)現(xiàn)涮雷,如果項目里有大量的視圖都是實現(xiàn)類似于上面這種獲取存儲在數(shù)據(jù)庫中的某個 Model 的列表的功能的話,會不斷地重復書寫諸如下面的代碼:

article_list = Article.objects.all()
context = { 'article_list' : article_list }
return render_to_response('blog/index.html',context)

就是不斷地獲取 Model 列表然后渲染模板文件轻局,Django 說寫多了開發(fā)人員就覺得無聊了洪鸭,那我們何不把這些邏輯抽象出來放到一個類里?于是 Django 幫我們寫好了一個類仑扑,專門用于處理上面的情況览爵,這就是 ListView,將上面的視圖函數(shù)轉(zhuǎn)寫成類視圖如下:

class IndexView(ListView):
    template_name = "blog/index.html"
    context_object_name = "article_list"

    def get_queryset(self):
        article_list = Article.objects.all()
        for article in article_list:
            article.body = markdown2.markdown(article.body, extras=['fenced-code-blocks'], )
        return article_list

    def get_context_data(self, **kwargs):
        kwargs['category_list'] = Category.objects.all().order_by('name')
        return super(IndexView, self).get_context_data(**kwargs)

首先看看 get_queryset 方法镇饮,它完成的功能和 article_list = Article.objects.all() 這句代碼類似蜓竹,獲取某個 Model 的列表(這里是文章列表),同時我們加入了自己的邏輯储藐,即對 article_list 中的各個 article 進行了 markdwon 拓展俱济,假如僅僅只需要獲取 article_list ,則甚至可以不用復寫 get_queryset 方法钙勃,只需指定一個 model 屬性蛛碌,告訴 Django 去獲取哪個 model 的列表就可以了,像這樣:

class IndexView(ListView):
    template_name = "blog/index.html"
    context_object_name = "article_list"
    model = Article

    def get_context_data(self, **kwargs):
        kwargs['category_list'] = Category.objects.all().order_by('name')
        return super(IndexView, self).get_context_data(**kwargs)

第二個復寫的方法是 get_context_data 方法肺缕,這個方法是用來給傳遞到模板文件的上下文對象(context)添加額外的內(nèi)容的(context 的概念在前面的教程中已有介紹左医,如果這里不懂的話我再簡單解釋一下授帕,我們在模板文件中會使用 {{ }} 這樣的標簽來包裹模板變量,這些變量哪里來的浮梢?就是視圖函數(shù)通過 context 傳遞到模板的)跛十。我們這里因為首頁需要顯示分類信息,因此我們把 category_list 通過 get_context_data 方法加入了 context 對象秕硝,視圖函數(shù)再幫我們把 context 傳遞給模板芥映。return super(IndexView, self).get_context_data(**kwargs) 語句的作用是添加了 category_list 到上下文中,還要把默認的一些上下文變量也返回給視圖函數(shù)远豺,以便其后續(xù)處理奈偏。

現(xiàn)在有了 model 列表,context躯护,按照視圖函數(shù)的邏輯應該是把這些傳遞給模板了惊来,ListView 通過指定 template_name 屬性來指定需要渲染的模板,而 context_object_name 是給 get_queryset 方法返回的 model 列表重新命名的棺滞,因為默認返回的 model 列表其名字是 object_list裁蚁,為了可讀性,我們可以通過 context_object_name 來重新指定继准,例如我們這里指定為 article_list枉证。

return render_to_response('blog/index.html',context) 的功能在 ListView 中 Django 已經(jīng)默認幫我們做了,翻看其源代碼就會知道:

... 省略其他代碼
def render_to_response(self, context, **response_kwargs):
        """
        Returns a response, using the `response_class` for this
        view, with a template rendered with the given context.

        If any keyword arguments are provided, they will be
        passed to the constructor of the response class.
        """
        response_kwargs.setdefault('content_type', self.content_type)
        return self.response_class(
            request=self.request,
            template=self.get_template_names(),
            context=context,
            using=self.template_engine,
            **response_kwargs
        )
... 省略其他代碼

如果你改變渲染模板的一些行為移必,通過復寫 render_to_response 方法即可室谚。

以上方法在類視圖調(diào)用 as_view() 方法后會被自動調(diào)用。

ListView 總結(jié)

  • ListView 主要用在獲取某個 model 列表中
  • 通過 template_name 屬性來指定需要渲染的模板崔泵,通過 context_object_name 屬性來指定獲取的 model 列表的名字秒赤,否則只能通過默認的 object_list 獲取
  • 復寫 get_queryset 方法以增加獲取 model 列表的其他邏輯
  • 復寫 get_context_data 方法來為上下文對象添加額外的變量以便在模板中訪問

DetailView

前面的 ListView 用于獲取某個 model 的列表,獲取的是一系列對象管削,但獲取單個 mdoel 對象也是很常見的倒脓,比如 Blog 里點擊某篇文章后進入文章的詳情頁,這里獲取的就是點擊這篇文章含思。我們通常會寫如下視圖函數(shù):

def detail(request,article_id):
    article = get_object_or_404(Article,pk=article_id)
    context = { 'article' : article }
    return render_to_response('blog/detail.html',context)

同樣的,如果這種需求多的話甘晤,開發(fā)人員就需要枯燥而乏味地大量重復寫 article = get_object_or_404(Article,pk=article_id) 這樣的句子含潘,Django 通過 DetailView 來把這種邏輯抽象出來,把上面的視圖函數(shù)轉(zhuǎn)成類視圖:

class ArticleDetailView(DetailView):
    model = Article
    template_name = "blog/detail.html"
    context_object_name = "article"
    pk_url_kwarg = 'article_id'

    def get_object(self, queryset=None):
        obj = super(ArticleDetailView, self).get_object()
        obj.body = markdown2.markdown(obj.body, extras=['fenced-code-blocks'], )
        return obj

model 屬性告訴 Django 是獲取哪個 model 對應的單個對象线婚,template_name遏弱,context_object_name 屬性和 ListView 中是一樣的作用,pk_url_kwarg 相當于視圖函數(shù)中的 article_id 參數(shù)塞弊,已告訴 Django 獲取的是 id 為多少的 model 實例漱逸。

get_object 方法默認情況下獲取 id 為pk_url_kwarg 的對象泪姨,如果需要在獲取過程中對獲取的對象做一些處理,比如對文章做 markdown 拓展饰抒,通過復寫 get_object 即可實現(xiàn)肮砾。

之后的處理就和 ListView 類似了,已經(jīng)實現(xiàn)了 render_to_response 方法來渲染模板袋坑。

以上方法在類視圖調(diào)用 as_view() 方法后會被自動調(diào)用仗处。

DetailView 總結(jié)

  • DetailView主要用在獲取某個 model 的單個對象中
  • 通過 template_name 屬性來指定需要渲染的模板,通過 context_object_name 屬性來指定獲取的 model 對象的名字枣宫,否則只能通過默認的 object 獲取
  • 復寫 get_object 方法以增加獲取單個 model 對象的其他邏輯
  • 復寫 get_context_data 方法來為上下文對象添加額外的變量以便在模板中訪問

使用類的通用視圖的好處

通過上面的例子你可能并未體會到使用類的通用視圖的好處婆誓,畢竟我們寫的基于函數(shù)的視圖似乎代碼量更短,但這僅僅是因為例子簡單而已也颤。同時別忘了洋幻,類是可以被繼承的,假如我們已經(jīng)寫好了一個基于類的通用視圖翅娶,要對其拓展功能鞋屈,只需繼承原本這個類視圖即可,而如果寫的是函數(shù)呢故觅?拓展性就沒有這么靈活厂庇,可能需要使用到裝飾器等高級技巧,或甚至不得不重復一段代碼到新拓展的視圖函數(shù)中输吏。但本質(zhì)上而言权旷,基于類的通用視圖依然是一個視圖函數(shù),因為最終調(diào)用時我們會通過 genericview.as_view() 方法把類視圖轉(zhuǎn)換成一般的視圖贯溅,url 配置是這樣的:

url(r'^article/(?P<article_id>\d+)$', views.ArticleDetailView.as_view(), name='detail'),

因此拄氯,基于類的視圖并非什么新的東西,只是為了方便而對一般的視圖另一種形式的改寫而已它浅,理解了一般的視圖寫法后译柏,通過閱讀其官方文檔和類視圖的源碼,很快就能掌握如何寫好類視圖了姐霍。以下就給出其參考資料:

Django學習小組簡介

django學習小組是一個促進 django 新手互相學習鄙麦、互相幫助的組織。

小組在一邊學習 django 的同時將一起完成幾個項目镊折,包括:

  • 一個簡單的 django 博客胯府,用于發(fā)布小組每周的學習和開發(fā)文檔;
  • django中國社區(qū)恨胚,為國內(nèi)的 django 開發(fā)者們提供一個長期維護的 django 社區(qū)骂因;

上面所說的這個社區(qū)類似于 segmentfault 和 stackoverflow ,但更加專注(只專注于 django 開發(fā)的問題)赃泡。

更多的信息請關注我們的 github 組織寒波,本教程項目的相關源代碼也已上傳到 GitHub 的 blog-tutorial 分支 上乘盼,請點擊鏈接獲取。

同時俄烁,你也可以加入我們的郵件列表 django_study@groups.163.com 绸栅,隨時關注我們的動態(tài)。我們會將每周的詳細開發(fā)文檔和代碼通過郵件列表發(fā)出猴娩。

如有任何建議阴幌,歡迎提 Issue,歡迎 fork卷中,pr矛双,當然也別忘了 star 哦!

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蟆豫,一起剝皮案震驚了整個濱河市议忽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌十减,老刑警劉巖栈幸,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異帮辟,居然都是意外死亡速址,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門由驹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芍锚,“玉大人,你說我怎么就攤上這事蔓榄〔⑴冢” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵甥郑,是天一觀的道長逃魄。 經(jīng)常有香客問我,道長澜搅,這世上最難降的妖魔是什么伍俘? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮店展,結(jié)果婚禮上养篓,老公的妹妹穿的比我還像新娘。我一直安慰自己赂蕴,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布舶胀。 她就那樣靜靜地躺著概说,像睡著了一般碧注。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上糖赔,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天萍丐,我揣著相機與錄音,去河邊找鬼放典。 笑死逝变,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的奋构。 我是一名探鬼主播壳影,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弥臼!你這毒婦竟也來了宴咧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤径缅,失蹤者是張志新(化名)和其女友劉穎掺栅,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纳猪,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡氧卧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了氏堤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沙绝。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丽猬,靈堂內(nèi)的尸體忽然破棺而出宿饱,到底是詐尸還是另有隱情,我是刑警寧澤脚祟,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布谬以,位于F島的核電站,受9級特大地震影響由桌,放射性物質(zhì)發(fā)生泄漏为黎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一行您、第九天 我趴在偏房一處隱蔽的房頂上張望铭乾。 院中可真熱鬧,春花似錦娃循、人聲如沸炕檩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笛质。三九已至泉沾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妇押,已是汗流浹背跷究。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敲霍,地道東北人俊马。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像肩杈,于是被迫代替她去往敵國和親柴我。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

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