本教程內容已過期,更新版教程請訪問: django 博客開發入門教程。css
經過前四周的時間咱們開發了一個簡單的我的 Blog,前幾期教程地址:html
第一週:Django 學習小組:博客開發實戰第一週教程 —— 編寫博客的 Model 和首頁面前端
第二週:Django 學習小組:博客開發實戰第二週教程 —— 博客詳情頁面和分類頁面python
第三週:Django 學習小組:博客開發實戰第三週教程 —— 文章列表分頁和代碼語法高亮git
番外:Django 學習小組:基於類的通用視圖詳解(一)github
第四周:Django 學習小組:博客開發實戰第四周——標籤雲與文章歸檔數據庫
本週咱們將實現 blog 文章的評論功能。django
提示:在閱讀教程的過程當中,若有任何問題請訪問咱們項目的 GithHub 或評論留言以獲取幫助,本教程的相關代碼已所有上傳在 Github。若是你對咱們的教程或者項目有任何改進建議,請您隨時告知咱們。更多交流請加入咱們的郵件列表 django_study@groups.163.com 和關注咱們在 GithHub 上的項目。編程
本文首發於編程派微信公衆號:編程派(微信號:codingpy)是一個專一Python編程的公衆號,天天更新有關Python的國外教程和優質書籍等精選乾貨,歡迎關注。segmentfault
首先須要爲評論(Comment)設計一個數據庫表,並編寫相應的 Model,將評論與文章關聯,再編寫發表評論的視圖,設置相應的 url 便可。
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('評論發表時間', 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:評論關聯的文章,由於一個評論只能關聯某一篇文章,而一篇文章下可能有多個評論,所以是一對多的關係,使用 ForeignKey
表單用來給服務器後臺提交用戶填寫的數據,例如平時咱們看到的填寫登陸、註冊信息的頁面就是一個登陸、註冊表單,用戶填寫表單信息後,點擊提交按鈕,表單中填寫的內容就會打包發送給服務器後臺。咱們須要爲用戶填寫評論設置一個表單,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 關聯的 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': '我來評兩句~'}), }
這裏咱們一如既往堅持使用基於類的通用視圖,因爲涉及到評論表單的提交處理,所以咱們使用 FormView。這裏對 FormView 的使用稍做講解。
在 Django 的基於函數的視圖中,涉及表單的處理的視圖其邏輯通常是這樣的:
def post_comment(request): if request.method =='POST': form = BlogCommentForm(request.POST) if form.is_valid(): ... else: ... else: ...
即,首先判斷用戶是否經過表單 POST 了數據過來,若是是,則根據 POST 過來的數據構建一個表單,若是數據驗證合法(form.is_valid),則建立評論,不然返回表單提交頁。若是沒有 POST 數據,則作其餘相應的事情。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): """提交的數據驗證合法後的邏輯""" # 首先根據 url 傳入的參數(在 self.kwargs 中)獲取到被評論的文章 target_article = get_object_or_404(Article, pk=self.kwargs['article_id']) # 調用ModelForm的save方法保存評論,設置commit=False則先不保存到數據庫, # 而是返回生成的comment實例,直到真正調用save方法時才保存到數據庫。 comment = form.save(commit=False) # 把評論和文章關聯 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): """提交的數據驗證不合法後的邏輯""" 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(), })
爲了方便地重定向回原來提交評論的文章詳情頁面,咱們爲文章(Article)的模型新增一個方法: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 視圖函數對應的 url return reverse('blog:detail', kwargs={'article_id': self.pk})
同時爲了在詳情頁渲染一個評論表單,稍微修改一下 ArticleDetailView 的視圖函數,把評論表單 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)
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 倉庫 更新相關的模板和靜態資源文件。
至此,整個評論功能的框架作好了,顯示效果以下:
固然這只是一個評論的框架,不少細節有待處理和完善,但不管如何,用戶能夠爲咱們的文章發表評論意見了。
django學習小組是一個促進 django 新手互相學習、互相幫助的組織。
小組在一邊學習 django 的同時將一塊兒完成幾個項目,包括:
一個簡單的 django 博客,用於發佈小組每週的學習和開發文檔;
django中國社區,爲國內的 django 開發者們提供一個長期維護的 django 社區;
上面所說的這個社區相似於 segmentfault 和 stackoverflow ,但更加專一(只專一於 django 開發的問題)。
更多的信息請關注咱們的 github 組織,本教程項目的相關源代碼也已上傳到 github 上。
同時,你也能夠加入咱們的郵件列表 django_study@groups.163.com ,隨時關注咱們的動態。咱們會將每週的詳細開發文檔和代碼經過郵件列表發出。
若有任何建議,歡迎提 Issue,歡迎 fork,pr,固然也別忘了 star 哦!