Django 學習小組:博客開發(fā)實戰(zhàn)第六周教程 —— 實現(xiàn)評論功能

本教程內容已過時,更新版教程請訪問: 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) blog 文章的評論功能。

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

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

實現(xiàn)思路

首先需要為評論(Comment)設計一個數(shù)據庫表捻爷,并編寫相應的 Model辈灼,將評論與文章關聯(lián),再編寫發(fā)表評論的視圖也榄,設置相應的 url 即可巡莹。

評論的 Model 設計

blog/models.py

class BlogComment(models.Model):
    user_name = models.CharField('評論者名字', max_length=100)
    user_email = models.EmailField('評論者郵箱', max_length=255)
    body = models.TextField('評論內容')
    created_time = models.DateTimeField('評論發(fā)表時間', auto_now_add=True)
    article = models.ForeignKey('Article', verbose_name='評論所屬文章', on_delete=models.CASCADE)

    def __str__(self):
        return self.body[:20]

參照大部分博客評論的樣式,我們的 BlogComment Model 包含這些字段:

user_name:用戶在評論前先要填寫他們想使用的昵稱

user_email:用戶在評論前先要填寫他們想使用的郵箱

body:用戶提交的評論內容

created_time:評論提交時間

article:評論關聯(lián)的文章甜紫,因為一個評論只能關聯(lián)某一篇文章榕莺,而一篇文章下可能有多個評論,因此是一對多的關系棵介,使用 ForeignKey

評論的表單

表單用來給服務器后臺提交用戶填寫的數(shù)據钉鸯,例如平時我們看到的填寫登錄、注冊信息的頁面就是一個登錄邮辽、注冊表單唠雕,用戶填寫表單信息后,點擊提交按鈕吨述,表單中填寫的內容就會打包發(fā)送給服務器后臺岩睁。我們需要為用戶填寫評論設置一個表單,django 的 form 模塊為我們提供了自動生成表單的功能揣云,如果對表單不熟悉請參閱:官方文檔:表單概述 捕儒,以了解基本的表單使用方法(如果你對表單感覺很陌生的話)。下面我們使用 Django 的 ModelForm ( django ModelForm 介紹 )類為我們自動生成表單邓夕。首先在 blog 目錄下新建一個 forms.py (和 models.py 同一目錄)文件用來存放 form 的代碼:

blog/forms.py

from django import forms
from .models import Article, BlogComment


class BlogCommentForm(forms.ModelForm):
    class Meta:
        """指定一些 Meta 選項以改變 form 被渲染后的樣式"""
        model = BlogComment # form 關聯(lián)的 Model
        
        fields = ['user_name', 'user_email', 'body']
        # fields 表示需要渲染的字段刘莹,這里需要渲染user_name、user_email焚刚、body
        # 這樣渲染后表單會有三個文本輸入框点弯,分別是輸入user_name、user_email矿咕、body的輸入框
        
        widgets = {
            # 為各個需要渲染的字段指定渲染成什么html組件抢肛,主要是為了添加css樣式。
            # 例如 user_name 渲染后的html組件如下:
            # <input type="text" class="form-control" placeholder="Username" aria-describedby="sizing-addon1">
            
            'user_name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': "請輸入昵稱",
                'aria-describedby': "sizing-addon1",
            }),
            'user_email': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': "請輸入郵箱",
                'aria-describedby': "sizing-addon1",
            }),
            'body': forms.Textarea(attrs={'placeholder': '我來評兩句~'}),
        }

視圖函數(shù)

這里我們一如既往堅持使用基于類的通用視圖碳柱,由于涉及到評論表單的提交處理捡絮,因此我們使用 FormView。這里對 FormView 的使用稍作講解莲镣。

在 Django 的基于函數(shù)的視圖中福稳,涉及表單的處理的視圖其邏輯一般是這樣的:

def post_comment(request):
    if request.method =='POST':
        form = BlogCommentForm(request.POST)
        if form.is_valid():
            ...
        else:
            ...
     else:
        ...

即,首先判斷用戶是否通過表單 POST 了數(shù)據過來剥悟,如果是灵寺,則根據 POST 過來的數(shù)據構建一個表單,如果數(shù)據驗證合法(form.is_valid)区岗,則創(chuàng)建評論略板,否則返回表單提交頁。如果沒有 POST 數(shù)據慈缔,則做其他相應的事情叮称。FormView 把這些邏輯做了整合,無需寫那么多 if else 語句:

blog/views.py

from django.views.generic.edit import FormView

...

class CommentPostView(FormView):
    form_class = BlogCommentForm # 指定使用的是哪個form
    template_name = 'blog/detail.html' 
    # 指定評論提交成功后跳轉渲染的模板文件藐鹤。
    # 我們的評論表單放在detail.html中瓤檐,評論成功后返回到原始提交頁面。

    def form_valid(self, form):
        """提交的數(shù)據驗證合法后的邏輯"""
        # 首先根據 url 傳入的參數(shù)(在 self.kwargs 中)獲取到被評論的文章
        target_article = get_object_or_404(Article, pk=self.kwargs['article_id'])
        
        # 調用ModelForm的save方法保存評論娱节,設置commit=False則先不保存到數(shù)據庫挠蛉,
        # 而是返回生成的comment實例,直到真正調用save方法時才保存到數(shù)據庫肄满。
        comment = form.save(commit=False)
        
        # 把評論和文章關聯(lián)
        comment.article = target_article
        comment.save()
        
        # 評論生成成功谴古,重定向到被評論的文章頁面,get_absolute_url 請看下面的講解稠歉。
        self.success_url = target_article.get_absolute_url()
        return HttpResponseRedirect(self.success_url)

    def form_invalid(self, form):
        """提交的數(shù)據驗證不合法后的邏輯"""
        target_article = get_object_or_404(Article, pk=self.kwargs['article_id'])
        
        # 不保存評論掰担,回到原來提交評論的文章詳情頁面
        return render(self.request, 'blog/detail.html', {
            'form': form,
            'article': target_article,
            'comment_list': target_article.blogcomment_set.all(),
        })

為了方便地重定向回原來提交評論的文章詳情頁面,我們?yōu)槲恼拢ˋrticle)的模型新增一個方法:get_absolute_url怒炸,調用該方法將得到該 Article 對應的 url带饱,例如這是文章 1 的 url:http://localhost:8000/article/1,則調用后返回 /article/1阅羹,這樣調用 HttpResponseRedirect 后將返回該 url 下的文章詳情頁勺疼。

blog/models.py

from django.core.urlresolvers import reverse

class Article(models.Model):
    STATUS_CHOICES = (
        ('d', 'Draft'),
        ('p', 'Published'),
    )

    ...
    
    class Meta:
        ordering = ['-last_modified_time']

    # 新增 get_absolute_url 方法
    def get_absolute_url(self):
        # 這里 reverse 解析 blog:detail 視圖函數(shù)對應的 url
        return reverse('blog:detail', kwargs={'article_id': self.pk})

同時為了在詳情頁渲染一個評論表單,稍微修改一下 ArticleDetailView 的視圖函數(shù)捏鱼,把評論表單 form 插入模板上下文中:

blog/views.py

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

    # 新增 form 到 context
    def get_context_data(self, **kwargs):
        kwargs['comment_list'] = self.object.blogcomment_set.all()
        kwargs['form'] = BlogCommentForm()
        return super(ArticleDetailView, self).get_context_data(**kwargs)

URL 設置

urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),
    ...
    # 設置評論視圖對應的 url
    url(r'^article/(?P<article_id>\d+)/comment/$', views.CommentPostView.as_view(), name='comment'),
]

設置模板文件

新增了一個 comment.html 文件以渲染評論表單和評論列表恢口,并且修改了 detail.html 文件以在文章詳情頁顯示評論表單和評論列表,修改了blog/tatic 下的 style.css 為評論添加樣式穷躁,由于代碼比較多耕肩,就不貼出來了,主要是 html 和 css 的前端相關代碼问潭,請到 GitHub 的 blog-tutorial 分支 更新相關的模板和靜態(tài)資源文件猿诸。

至此,整個評論功能的框架做好了狡忙,顯示效果如下:

評論效果演示圖

當然這只是一個評論的框架梳虽,很多細節(jié)有待處理和完善,但無論如何灾茁,用戶可以為我們的文章發(fā)表評論意見了窜觉。

Django學習小組簡介

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

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

  • 一個簡單的 django 博客旬陡,用于發(fā)布小組每周的學習和開發(fā)文檔;
  • django中國社區(qū)语婴,為國內的 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 哦蔬螟!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末此迅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子旧巾,更是在濱河造成了極大的恐慌耸序,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鲁猩,死亡現(xiàn)場離奇詭異坎怪,居然都是意外死亡,警方通過查閱死者的電腦和手機廓握,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門搅窿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人隙券,你說我怎么就攤上這事男应。” “怎么了娱仔?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵沐飘,是天一觀的道長。 經常有香客問我,道長耐朴,這世上最難降的妖魔是什么借卧? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮筛峭,結果婚禮上铐刘,老公的妹妹穿的比我還像新娘。我一直安慰自己蜒滩,他們只是感情好,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布奶稠。 她就那樣靜靜地躺著俯艰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郎汪。 梳的紋絲不亂的頭發(fā)上攻旦,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天峦阁,我揣著相機與錄音,去河邊找鬼啦辐。 笑死,一個胖子當著我的面吹牛蜈项,可吹牛的內容都是我干的芹关。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼紧卒,長吁一口氣:“原來是場噩夢啊……” “哼侥衬!你這毒婦竟也來了?” 一聲冷哼從身側響起跑芳,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤轴总,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后博个,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怀樟,經...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年盆佣,在試婚紗的時候發(fā)現(xiàn)自己被綠了往堡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡共耍,死狀恐怖投蝉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情征堪,我是刑警寧澤瘩缆,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站佃蚜,受9級特大地震影響庸娱,放射性物質發(fā)生泄漏着绊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一熟尉、第九天 我趴在偏房一處隱蔽的房頂上張望归露。 院中可真熱鬧,春花似錦斤儿、人聲如沸剧包。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疆液。三九已至,卻和暖如春陕贮,著一層夾襖步出監(jiān)牢的瞬間堕油,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工肮之, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掉缺,地道東北人。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓戈擒,卻偏偏與公主長得像眶明,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子筐高,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內容