在沒有互聯網的年代,我們用日記來記錄每天的心得體會。小的時候我有一個帶鎖的日記本,生怕被別人看見裏面寫了啥,鑰匙藏得那叫一個絕。
現在時代變了,網絡版的日記本:博客,卻巴不得越多人看越好。
別人看完你寫的深度好文,難免也想高談闊論一番,這就是“評論”功能。
本章將要編寫的評論模塊,幾乎沒有新的知識點,而是將前面章節內容的綜合應用。
強烈建議讀者自行嘗試編寫這部分內容,測試自己的知識掌握程度。
準備工作
評論是一個相對獨立的功能,因此新建一個評論的app:
(env) E:\django_project\my_blog > ppython manage.py startapp comment
有的人覺得奇怪,沒有博文就沒有評論,爲什麼說評論是“獨立”的功能?
那是因爲不僅博文可以評論,照片、視頻甚至網站本身都可以“被評論”。將其封裝成單獨的模塊方便以後的擴展。
確認app創建成功後,記得在settings.py
中註冊:
my_blog/settings.py
...
INSTALLED_APPS = [
...
'comment',
]
...
TIME_ZONE = 'Asia/Shanghai'
...
因爲我們想顯示發表評論的時間,修改時區設置TIME_ZONE
爲上海的時區。
然後在my_blog/urls.py
中註冊根路由:
my_blog/urls.py
...
urlpatterns = [
...
# 評論
path('comment/', include('comment.urls', namespace='comment')),
]
...
編寫核心功能
評論的模型
首先編寫評論的模型:
comment/models.py
from django.db import models
from django.contrib.auth.models import User
from article.models import ArticlePost
# 博文的評論
class Comment(models.Model):
article = models.ForeignKey(
ArticlePost,
on_delete=models.CASCADE,
related_name='comments'
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='comments'
)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def __str__(self):
return self.body[:20]
模型中共有2個外鍵:
article
是被評論的文章user
是評論的發佈者
別忘了每次新增、修改Model後,必須數據遷移。
提示:你必須先在setting.py中註冊app,這個app中的數據遷移才能生效
評論的表單
用戶提交評論時會用到表單,因此新建表單類:
comment/forms.py
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['body']
因爲模型中的2個外鍵將通過視圖邏輯自動填寫,所以這裏只需要提交body
就足夠了。
評論的url
在comment app中新建路由文件:
comment/urls.py
from django.urls import path
from . import views
app_name = 'comment'
urlpatterns = [
# 發表評論
path('post-comment/<int:article_id>/', views.post_comment, name='post_comment'),
]
評論必須關聯在某篇具體的博文裏,因此傳入博文的id
,方便後續調用。
post_comment()
視圖還沒寫,先取個名字佔位置。
評論的視圖
評論的視圖函數如下:
comment/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from article.models import ArticlePost
from .forms import CommentForm
# 文章評論
@login_required(login_url='/userprofile/login/')
def post_comment(request, article_id):
article = get_object_or_404(ArticlePost, id=article_id)
# 處理 POST 請求
if request.method == 'POST':
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.article = article
new_comment.user = request.user
new_comment.save()
return redirect(article)
else:
return HttpResponse("表單內容有誤,請重新填寫。")
# 處理錯誤請求
else:
return HttpResponse("發表評論僅接受POST請求。")
代碼中有2個新面孔。
get_object_or_404()
:它和Model.objects.get()
的功能基本是相同的。區別是在生產環境下,如果用戶請求一個不存在的對象時,Model.objects.get()
會返回Error 500
(服務器內部錯誤),而get_object_or_404()
會返回Error 404
。相比之下,返回404錯誤更加的準確。
redirect()
:返回到一個適當的url中:即用戶發送評論後,重新定向到文章詳情頁面。當其參數是一個Model對象時,會自動調用這個Model對象的get_absolute_url()
方法。因此接下來馬上修改article
的模型。
實際上之前的章節已經用過
redirect()
了。功能是相同的,實現上略有區別。
文章的模型
按照上面說的,在文章模型中添加get_absolute_url()
方法:
article/models.py
...
# 記得導入
from django.urls import reverse
class ArticlePost(models.Model):
...
# 獲取文章地址
def get_absolute_url(self):
return reverse('article:article_detail', args=[self.id])
通過reverse()
方法返回文章詳情頁面的url,實現了路由重定向。
文章詳情視圖
評論模塊需要在文章詳情頁面展示,所以必須把評論模塊的上下文也傳遞到模板中。
因此修改article/views.py
中的article_detail()
:
article/views.py
...
from comment.models import Comment
def article_detail(request, id):
# 已有代碼
article = ArticlePost.objects.get(id=id)
# 取出文章評論
comments = Comment.objects.filter(article=id)
...
# 添加comments上下文
context = { 'article': article, 'toc': md.toc, 'comments': comments }
...
filter()
可以取出多個滿足條件的對象,而get()
只能取出1個,注意區分使用
文章詳情模板
到最後一步了,堅持。所有後臺的功能已經寫完了,就差把所有這些展現到頁面中了。
修改文章詳情頁面:
templates/article/detail.html
...
<div class="col-9">
...
<!-- 已有代碼,文章正文 -->
<div class="col-12">
...
</div>
<!-- 發表評論 -->
<hr>
{% if user.is_authenticated %}
<div>
<form
action="{% url 'comment:post_comment' article.id %}"
method="POST"
>
{% csrf_token %}
<div class="form-group">
<label for="body">
<strong>
我也要發言:
</strong>
</label>
<textarea
type="text"
class="form-control"
id="body"
name="body"
rows="2"></textarea>
</div>
<!-- 提交按鈕 -->
<button type="submit" class="btn btn-primary ">發送</button>
</form>
</div>
<br>
{% else %}
<br>
<h5 class="row justify-content-center">
請<a href="{% url 'userprofile:login' %}">登錄</a>後回覆
</h5>
<br>
{% endif %}
<!-- 顯示評論 -->
<h4>共有{{ comments.count }}條評論</h4>
<div>
{% for comment in comments %}
<hr>
<p>
<strong style="color: pink">
{{ comment.user }}
</strong> 於
<span style="color: green">
{{ comment.created|date:"Y-m-d H:i:s" }}
</span> 時說:
</p>
<pre style="font-family: inherit; font-size: 1em;">
{{ comment.body }}</pre>
{% endfor %}
</div>
</div>
<!-- 目錄 -->
<div class="col-3 mt-4">
...
</div>
...
-
表單組件中的
action
指定數據提交到哪個url中 -
顯示評論中的
comments.count
是模板對象中內置的方法,對包含的元素進行計數 -
|date:"Y-m-d H:i:s"
:管道符你已經很熟悉了,用於給對象“粘貼”某些屬性或功能。這裏用於格式化日期的顯示方式。請嘗試修改其中的某些字符試試效果。 -
<pre>
定義預格式化的文本,在我們的項目中最關鍵的作用是保留空格和換行符。該標籤會改變文字的字體、大小等,因此用style
屬性重新定義相關內容。嘗試將<pre>
替換爲div
,輸入多行文本試試效果。之前說代碼最好不要複製粘貼,否則有些“小坑”你是留意不到的。比如在
<pre>
標籤中的文本千萬不能縮進。
測試
又到了激動人心的測試環節了。
登錄自己的賬戶,進入某個文章詳情頁面,發現已經可以進行留言了:
如果退出登錄,顯示提示語:
點擊登錄就回到登錄頁面。
評論模塊的發佈、展示功能就搞定了。
掃尾工作
數據的刪、改功能我們已經做過很多遍,這裏不打算再贅述了。
評論同樣也可以支持Markdown語法,或者插入Emoji表情符號。
讀者可以自己去實現感興趣的功能。
有些網站乾脆就沒有刪除、更新評論的功能。因爲對小站來說,這些功能用到的次數太少太少了,不如把精力用在更有價值的地方去。比如我的博客就沒有。
還有的網站提供軟刪除,刪除後僅僅是不顯示而已,實際上數據還存在。
具體應該如何做,都以你的喜好而定。
總結
本章實現了發表評論、展示評論的功能。像開頭說的一樣,本章的內容是前面所學章節的綜合。
如果你沒有看本章代碼,而是完全靠自己完成了評論功能,那麼恭喜你獲得了**“Django入門程序員”**的稱號!不要小看“入門”兩字,萬事開頭難嘛。
- 有疑問請在杜賽的個人網站留言,我會盡快回復。
- 或Email私信我:[email protected]
- 項目完整代碼:Django_blog_tutorial
轉載請註明出處。