Python Django 學習紀錄:架設個人部落格(三)
評論功能與用戶登錄
一.、實現評論功能的方式
1.第三方社會化評論插件:
臉書、推特、微博...等第三方登錄
※可能因為第三方服務器出問題而導致插件也出問題
2.Django 評論庫:
使用 django-comment 實現
3.自己寫代碼實現:
可以依據開發者的需求,做更細部的調整
二.、創建評論模型
1.模型建構
- 評論對象
- 評論內容
- 評論時間
- 評論者
2.創建應用模型:
開啟虛擬環境,創建一個名為comment的應用:
python manage.py startapp comment
開啟<models.py> 創建 comment 模型
這邊要導入 contenttypes 框架 和 User模型,並添加幾條資料模型:
from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import User # Create your models here. class Comment(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) #透過外鍵指向模型 object_id = models.PositiveIntegerField() #紀錄對應模型的主鍵值 content_object = GenericForeignKey('content_type', 'object_id') #集合前兩項成一個通用外鍵 text = models.TextField() comment_time = models.DateTimeField(auto_now_add=True) user = models.ForeignKey(User, on_delete=models.CASCADE) class Meta: ordering = ['-comment_time']
在<admin.py>中註冊:
from django.contrib import admin from .models import Comment # Register your models here. @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ('content_object', 'text', 'comment_time', 'user')
在<settings.py>的應用列表中添加 comment:
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'ckeditor', 'ckeditor_uploader', 'blog', 'read_statistic', 'comment', ]
記得進行數據庫遷移 (makemigrations)與應用 (migrate)
3.設置<urls.py>路徑:
在 comment 應用目錄創建<urls.py>文件, 用來指向提交評論的路徑:
path('comment/',include('comment.urls')),
在 mysite 專案目錄中的<urls.py>文件, 添加 comment 應用的路徑:
from django.urls import path
4.初步撰寫提交評論方法:
在comment 應用中,創建 <views.py>文件
並在其中撰寫提交評論並儲存的方法:
from django.shortcuts import render, redirect from .models import Comment from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.urls import reverse # Create your views here. def update_comment(request): user = request.user #取得用戶信息 text = request.POST.get('text','') #取得提交的評論,若無則提交空白 content_type = request.POST.get('content_type','') object_id = int(request.POST.get('object_id','')) model_class = ContentType.objects.get(model=content_type).model_class() #取得模型中的class model_obj = model_class.objects.get(pk=object_id) comment = Comment() comment.user = user comment.text = text comment.content_object = model_obj comment.save() referer = request.META.get('HTTP_REFERER', reverse('update_comment')) return redirect(referer)
5.初步建立前端評論區域:
評論區域將添加在文章詳細頁面<blog_detail.html>中
以下為初步的評論區域顯示:
<div class="row"> <div class="col-xs-10 col-xs-offset-1"> <div class="comment-area"> <h3 class="comment-area_title">提交評論</h3> {% if user.is_authenticated %} {{ user.username }},歡迎評論 <form action="{% url 'update_comment' %}" method="POST"> {% csrf_token %} <textarea id="comment_text" name="text"></textarea> <input type="hidden" name="content_type" value="blog"> <input type="hidden" name="object_id" value="{{ blog.pk }}"> <input type="submit" value="評論"> </form> {% else %} 您尚未登錄,登錄之後方可評論 <a href="{% url 'login' %}">登錄</a> {% endif %} </div> <div style="margin-top: 2em;border:1px dashed;padding:2em;margin-bottom: 2em;">評論列表區域</div> </div> </div>
三.、使用Django Form設置用戶登錄
由於這部分細節較多,除了表單模型外還有登錄驗證的部分要同時處理
所以這裡先傳送基本的表單到前台展示,接著才加入驗證登錄的細節
1.創建 <forms.py> 文件:
在 mysite 專案目錄中,創建 <forms.py> 文件
導入 Django 的form 套件,並為用戶登錄設置一個表單:
from django import forms class LoginForm(forms.Form): username = forms.CharField() password = forms.CharField()
2.設置<urls.py>路徑:
在 mysite 專案目錄中的<urls.py>, 添加 login 路徑:
path('login/', views.login, name='login'),
3.初步撰寫後端登錄方法:
在 mysite 專案目錄中的<views.py>, 添加 login 方法:
from .forms import LoginForm #導入登錄表單 def login(request): login_form = LoginForm() context ={} context['login_form'] = login_form return render(request, "login.html", context)
4.初步撰寫登錄前端頁面:
在<blog_detail.html>的評論區域,添加進行登錄的連結:
<div class="row"> <div class="col-xs-10 col-xs-offset-1"> <div class="comment-area"> <h3 class="comment-area_title">提交評論</h3> {% if user.is_authenticated %} {{ user.username }},歡迎評論 <form action="{% url 'update_comment' %}" method="POST"> {% csrf_token %} <textarea id="comment_text" name="text"></textarea> <input type="hidden" name="content_type" value="blog"> <input type="hidden" name="object_id" value="{{ blog.pk }}"> <input type="submit" value="評論"> </form> {% else %} 您尚未登錄,登錄之後方可評論 <a href="{% url 'login' %}">登錄</a> {% endif %} </div> <div style="margin-top: 2em;border:1px dashed;padding:2em;margin-bottom: 2em;">評論列表區域</div> </div> </div>
在 mysite 的 templates 目錄創建一個<login.html>,顯示登錄頁面:
{% extends "base.html" %} {% load staticfiles %} {% block title %} 登錄頁面 {% endblock %} {% block nav_blog_active %}active{% endblock %} {% block content %} <form action="{% url 'login' %}" method="POST"> {% csrf_token %} {{ login_form }} <input type="submit" value="登錄"> </form> {% endblock %}
5.完善後端登錄方法
前面已經將登錄系統的雛形建立好了
接下來就是加入登錄驗證、傳送數據來完善 login 方法
這裡將登錄驗證的部分移到表單中進行,所以只進行驗證成功後的處理
註解寫在程式碼之中,以下為代碼:
def login(request): if request.method == 'POST': #檢查是否有POST數據 login_form = LoginForm(request.POST) #提交POST數據 if login_form.is_valid(): #若表單有效 user = login_form.cleaned_data['user'] #取得用戶資料 auth.login(request, user) #進行登錄 return redirect(request.GET.get('from',reverse('home'))) #跳轉回前一個頁面 else: #若沒有POST數據 login_form = LoginForm() #加載頁面 context = {} context['login_form'] = login_form return render(request, "login.html", context)
6.修改登錄後前端頁面
由於這裡將登錄後頁面改為跳轉至前一個頁面
所以要修改前端<blog_detail.html>與<login.html>模板
使用get的方法,使登錄後跳轉至登錄前頁面
<blog_detail.html>模板的url修改:
<div class="row"> <div class="col-xs-10 col-xs-offset-1"> <div class="comment-area"> <h3 class="comment-area_title">提交評論</h3> {% if user.is_authenticated %} {{ user.username }},歡迎評論 <form action="{% url 'update_comment' %}" method="POST"> {% csrf_token %} <textarea id="comment_text" name="text"></textarea> <input type="hidden" name="content_type" value="blog"> <input type="hidden" name="object_id" value="{{ blog.pk }}"> <input type="submit" value="評論"> </form> {% else %} 您尚未登錄,登錄之後方可評論 <a href="{% url 'login' %}?from={{ request.get_full_path }}">登錄</a> {% endif %} </div> <div style="margin-top: 2em;border:1px dashed;padding:2em;margin-bottom: 2em;">評論列表區域</div> </div> </div>
<loginl>模板則是將 action (傳送資料的目的地)移除,使登錄後能正確導向登錄前頁面:
{% extends "base.html" %} {% load staticfiles %} {% block title %} 登錄頁面 {% endblock %} {% block nav_blog_active %}active{% endblock %} {% block content %} <form action="" method="POST"> {% csrf_token %} {{ login_form }} <input type="submit" value="登錄"> </form> {% endblock %}
6.在表單中進行登錄驗證
在LoginForm表單類別中,添加一個驗證用戶資料的方法
若驗證失敗則會拋出錯誤訊息到前端顯示
驗證成功則將用錄資料回傳到 login 方法進行登錄
註解寫在程式碼之中,以下為代碼:
from django import forms from django.contrib import auth from django.urls import reverse class LoginForm(forms.Form): username = forms.CharField(label='用戶名', required=True) #label修改標籤文字,required設置是否接受空白內容 password = forms.CharField(label='密碼',widget=forms.PasswordInput) #widget定義標籤內容,這裡將密碼改為密文 def clean(self): #在forms中進行驗證 username = self.cleaned_data['username'] password = self.cleaned_data['password'] user = auth.authenticate(username=username, password=password) if user is None: #若驗證失敗 raise forms.ValidationError('用戶名或密碼不正確') #拋出錯誤訊息 else: #若驗證成功 self.cleaned_data['user'] = user #返回登錄資料 return self.cleaned_data
7.美化 Form 表單顯示的資料
在form可以添加修改標籤中的屬性
搭配 bootstrap 可以達到美化表單的效果
(1)在 LoginForm表單 添加個別屬性調整
以下為代碼:
from django import forms from django.contrib import auth from django.urls import reverse class LoginForm(forms.Form): #label修改標籤文字,required不寫則預設接受空白內容,attrs添加bootstrap屬性,placeholder設置預設文字 username = forms.CharField(label='用戶名', widget=forms.TextInput( attrs={'class':'form-control', 'placeholder':'請輸入用戶名'})) #widget定義標籤內容,這裡將密碼改為密文,attrs添加bootstrap屬性,placeholder設置預設文字 password = forms.CharField(label='密碼', widget=forms.PasswordInput( attrs={'class':'form-control', 'placeholder':'請輸入密碼'})) def clean(self): #在forms中進行驗證 username = self.cleaned_data['username'] password = self.cleaned_data['password'] user = auth.authenticate(username=username, password=password) if user is None: #若驗證失敗 raise forms.ValidationError('用戶名或密碼不正確') #拋出錯誤訊息 else: #若驗證成功 self.cleaned_data['user'] = user #返回登錄資料 return self.cleaned_data
(2)在<login.html>進行bootstrap美化
這裡添加了容器、響應式設計、面板標題與內容框架
而為了把用戶名與密碼後面的冒號去除,所以使用for迴圈對表單又進行了過濾
以下為代碼:
{% extends "base.html" %} {% load staticfiles %} {% block title %} 登錄頁面 {% endblock %} {% block nav_blog_active %}active{% endblock %} {% block content %} <div class="container"> <div class="row"> <!-- 響應式 --> <div class="col-xs-12 col-md-4 col-md-offset-4"> <!-- 添加面板 --> <div class="panel panel-default"> <!-- 面板標題 --> <div class="panel-heading">登錄</div> <div class="panel-body"> <!-- 表單 --> <form action="" method="POST"> {% csrf_token %} <!-- 進行表單細部調整 --> {% for field in login_form %} <!-- 移除冒號 --> <label for="{{ field.id_for_label }}">{{field.label}}</label> {{ field }} <!-- 錯誤訊息紅字顯示 --> <p class="text-danger">{{ field.errors.as_text }}</p> {% endfor %} <span class="pull-left text-danger">{{ login_form.non_field_errors }}</span> <input type="submit" value="登錄" class="btn btn-primary pull-right"> </form> </div> </div> </div> </div> </div> {% endblock %}
(3)範例展示
下圖為目前登入頁面的範例展示
四.、設置用戶註冊
已經實作了登錄功能,接下來就能如法炮製的快速實現註冊功能
1.創建 註冊表單:
在 mysite 專案目錄中的 <forms.py> 文件
為用戶註冊設置一個表單:
class RegForm(forms.Form): username = forms.CharField(label='用戶名', required=False, max_length=20, min_length=3, widget=forms.TextInput( attrs={'class':'form-control', 'placeholder':'請輸入3-20位用戶名'})) email = forms.EmailField(label='信箱', required=False, widget=forms.EmailInput( attrs={'class':'form-control', 'placeholder':'請輸信箱'})) password = forms.CharField(label='密碼', max_length=12, min_length=6, widget=forms.PasswordInput( attrs={'class':'form-control', 'placeholder':'請輸入6-12位密碼'})) password_again = forms.CharField(label='再次輸入密碼', max_length=12, min_length=6, widget=forms.PasswordInput( attrs={'class':'form-control', 'placeholder':'請再次輸入密碼'})) def clean_username(self): username = self.cleaned_data['username'] if User.objects.filter(username=username).exists(): raise forms.ValidationError('用戶名稱已存在') return username def clean_email(self): email = self.cleaned_data['email'] if User.objects.filter(email=email).exists(): raise forms.ValidationError('信箱已存在') return email def clean_password_again(self): password = self.cleaned_data['password'] password_again = self.cleaned_data['password_again'] if password != password_again: #比對密碼是否相同 raise forms.ValidationError('兩次輸入密碼不一致') return password
2.設置<urls.py>路徑:
在 mysite 專案目錄中的<urls.py>, 添加 register 路徑:
path('register/', views.register, name='register'),
3.撰寫後端註冊方法:
在 mysite 專案目錄中的<views.py>, 添加 register 方法:
def register(request): if request.method == 'POST': reg_form = RegForm(request.POST) if reg_form.is_valid(): username = reg_form.cleaned_data['username'] email = reg_form.cleaned_data['email'] password = reg_form.cleaned_data['password'] #創建用戶 user = User() #創建物件 user.username = username user.email = email user.set_password(password) #儲存密文密碼 user.save() #將用戶資料儲存至資料庫 #登錄用戶 user = auth.authenticate(username=username, password=password) auth.login(request, user) return redirect(request.GET.get('from',reverse('home'))) else: reg_form = RegForm() context = {} context['reg_form'] = reg_form return render(request, "register.html", context)
4.撰寫註冊頁面:
在<blog_detail.html>的評論區域,添加進行註冊的連結:
<div class="comment-area"> <h3 class="comment-area_title">提交評論</h3> {% if user.is_authenticated %} {{ user.username }},歡迎評論 <form action="{% url 'update_comment' %}" method="POST"> {% csrf_token %} <textarea id="comment_text" name="text"></textarea> <input type="hidden" name="content_type" value="blog"> <input type="hidden" name="object_id" value="{{ blog.pk }}"> <input type="submit" value="評論"> </form> {% else %} 您尚未登錄,登錄之後方可評論 <a href="{% url 'login' %}?from={{ request.get_full_path }}">登錄</a> <a href="{% url 'register' %}?from={{ request.get_full_path }}">註冊</a> {% endif %} </div>
在 mysite 的 templates 目錄創建一個<register.html>,顯示登錄頁面:
{% extends "base.html" %} {% load staticfiles %} {% block title %} 註冊頁面 {% endblock %} {% block nav_blog_active %}active{% endblock %} {% block content %} <div class="container"> <div class="row"> <!-- 響應式 --> <div class="col-xs-12 col-md-4 col-md-offset-4"> <!-- 添加面板 --> <div class="panel panel-default"> <!-- 面板標題 --> <div class="panel-heading">註冊</div> <div class="panel-body"> <!-- 表單 --> <form action="" method="POST"> {% csrf_token %} <!-- 進行表單細部調整 --> {% for field in reg_form %} <!-- 移除冒號 --> <label for="{{ field.id_for_label }}">{{field.label}}</label> {{ field }} <!-- 錯誤訊息紅字顯示 --> <p class="text-danger">{{ field.errors.as_text }}</p> {% endfor %} <span class="pull-left text-danger">{{ reg_form.non_field_errors }}</span> <input type="submit" value="註冊" class="btn btn-primary pull-right"> </form> </div> </div> </div> </div> </div> {% endblock %}
四.、評論表單化、富文本編輯
前面只建立了評論功能的雛型,接下來要使用 form 表單來完善評論功能
並結合表單提交與富文本編輯,讓評論的編輯器更加豐富
最後再採用ajax異步刷新的方式,使評論功能更貼近用戶需求
1.創建評論表單:
在 comment 應用目錄中創建 <forms.py>
並在其中添加一個 CommentForm 表單,用來傳送評論內容
其中因為需要再表單中驗證用戶,所以需自訂user用法,再由update_comment傳入user參數來使用
以下為代碼:
from django import forms from django.contrib.contenttypes.models import ContentType from django.db.models import ObjectDoesNotExist class CommentForm(forms.Form): #由於Comment方法的通用性,這裡也要沿用ContentType框架 content_type = forms.CharField(widget=forms.HiddenInput) object_id = forms.IntegerField(widget=forms.HiddenInput) text = forms.CharField(widget=forms.Textarea) #修改類別,將user寫入方法 def __init__(self, *args, **kwargs): if 'user' in kwargs: self.user = kwargs.pop('user') super(CommentForm, self).__init__(*args, **kwargs) def clean(self): #判斷用戶是否登錄 if self.user.is_authenticated: self.cleaned_data['user'] = self.user else: raise forms.ValidationError('用戶尚未登錄') #評論對象驗證 content_type = self.cleaned_data['content_type'] object_id = self.cleaned_data['object_id'] text = self.cleaned_data['text'] try: model_class = ContentType.objects.get(model=content_type).model_class() #取得模型中的評論方法 model_obj = model_class.objects.get(pk=object_id) #取得評論對應的編號 self.cleaned_data['content_object'] = model_obj except ObjectDoesNotExist: #若對象不存在 raise forms.ValidationError('評論對象不存在') return self.cleaned_data
2.撰寫後端評論方法:
(1)修改 blog_detail方法
在 blog 應用<views.py>中的 blog_detail 方法中
添加傳遞後端評論資料的代碼
導入ContentTypes框架與 Comment應用的comment模型
其中也使用到initail初始化,來輔助form表單傳送文章類型與編號:
from comment.forms import CommentForm from django.contrib.contenttypes.models import ContentType from comment.models import Comment def blog_detail(request, blog_pk): #導入 blog_pk 變數 context={} #建立字典 blog = get_object_or_404(Blog, pk=blog_pk) #獲取blog_pk read_cookie_key = read_statistic_once_read(request, blog) #使用閱讀計數 #資料庫文章日期已經為由新到舊排列(新文章日期大於舊文章日期) #獲取前一篇文章,取得大於該文章的日期中的最後一篇 context['previous_blog'] = Blog.objects.filter(created_time__gt=blog.created_time).last() #獲取後一篇文章,取得小於該文章的日期中的第一篇 context['next_blog'] = Blog.objects.filter(created_time__lt=blog.created_time).first() context['blog'] = blog #獲取評論 #取得該評論對應的文章類型與編號 blog_content_type = ContentType.objects.get_for_model(blog) comments = Comment.objects.filter(content_type=blog_content_type, object_id=blog.pk) #使用initial在運行時聲明表單字段的初始值,將文章類型與編號賦值 data={} data['content_type']= blog_content_type.model data['object_id'] = blog_pk context['comment_form'] = CommentForm(initial=data) context['comments'] = comments response = render(request, 'blog/blog_detail.html', context) #將字典返回到'blog_detail.html' response.set_cookie(read_cookie_key, 'true') #閱讀cookie return response
(2)修改 update_comment方法
修改 comment應用<views.py>中的update_comment方法
將其改為接收form表單資料後,傳送資料至前端頁面:
from django.shortcuts import render, redirect from .models import Comment from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.urls import reverse from .forms import CommentForm # Create your views here. def update_comment(request): referer = request.META.get('HTTP_REFERER', reverse('update_comment')) comment_form = CommentForm(request.POST,user=request.user)#傳送user參數 if comment_form.is_valid(): #如果檢查通過 comment = Comment() comment.user = comment_form.cleaned_data['user'] comment.text = comment_form.cleaned_data['text'] comment.content_object = comment_form.cleaned_data['content_object'] comment.save() return redirect(referer) else: return redirect('home.html')
3.撰寫前端評論頁面:
在 <blog_detail.html>前端頁面改為使用表單提交評論
並增加評論列表的顯示,並用safe將html碼實現以因應富文本編輯:
<div class="row"> <div class="col-xs-10 col-xs-offset-1"> <div class="comment-area"> <h3 class="comment-area_title">提交評論</h3> {% if user.is_authenticated %} <form action="{% url 'update_comment' %}" method="POST" style="overflow:hidden"> {% csrf_token %} <label>{{ user.username }},歡迎評論</label> {% for field in comment_form %} {{ field }} {% endfor %} <input type="submit" value="評論" class="btn btn-primary pull-right" style="float:right"> </form> {% else %} 您尚未登錄,登錄之後方可評論 <a class="btn btn-primary" href="{% url 'login' %}?from={{ request.get_full_path }}">登錄</a> <a class="btn btn-primary" href="{% url 'register' %}?from={{ request.get_full_path }}">註冊</a> {% endif %} </div> <div class="comment-area"> <h3 class="comment-area_title">評論列表</h3> {% for comment in comments %} <div> {{ comment.user }} ({{ comment.comment_time|date:"Y-m-d H:n:s" }}): {{comment.text |safe }} </div> {% empty %} 暫無評論 {% endfor %} </div> </div
4.富文本編輯評論:
(1)後端導入功能
在 comment 應用的 <forms.py>頁面導入表單用的富文本編輯套件並使用在評論內容欄位
以下為代碼:
from ckeditor.widgets import CKEditorWidget text = forms.CharField(widget=CKEditorWidget())
如果要自定義富文本編輯的功能,可以詳閱此文檔進行調整
以下為自定義後的代碼:
from ckeditor.widgets import CKEditorWidget text = forms.CharField(widget=CKEditorWidget(config_name='comment_ckeditor'))
在<settings.py>中,進行自定義的設置:
#配置ckeditor CKEDITOR_CONFIGS = { 'default':{}, 'comment_ckeditor': { 'toolbar': 'custom', 'toolbar_custom': [ ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript'], ["TextColor", "BGColor", 'RemoveFormat'], ['NumberedList', 'BulletedList'], ['Link', 'Unlink'], ["Smiley", "SpecialChar", 'Blockquote'], ], 'width': 'auto', 'height': '180', 'tabSpaces': 4, 'removePlugins': 'elementspath', 'resize_enabled': False, } }
(2)前端導入js文件
在前端頁面也需導入js文件才可使用
在<blog_detail.html>預留的擴充模塊中加入js文件的位址:
{% block header_extends %} <link rel="stylesheet" href="{% static 'css/blog.css' %}"> <script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script> <script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script> {% endblock %}
(3)修改CSS樣式
檢視HTML網頁可以找到ckeditor的div標籤為"django-ckeditor-widget"
因此可以對此在<blog.css>進行樣式修改:
div.django-ckeditor-widget{ width:100%; }
五.、評論 ajax異步刷新
一般提交資料會刷新頁面
ajax提交則是一種不刷新頁面的異步刷新方式
可以參考此中文文檔
1.添加 ajax 的 js 代碼:
在<base.html>下方增加一個js擴充模塊
必須加在導入的js文件後,以下為代碼:
<!-- 導入jquery和bootstrap(jquery必須在前) --> <script type="text/javascript" src="{% static 'jquery-3.3.1.min.js' %}"></script> <script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script> {% block script_extends %}{% endblock %}
2.為Html標籤添加id:
使用ajax需要使用Html標籤的id屬性來查找對應的內容
所以在需用到ajax的部分添加id屬性,並添加一條顯示錯誤訊息的html代碼
以下為<blog_detail.html>評論區域所需修改的部分:
<div class="row"> <div class="col-xs-10 col-xs-offset-1"> <div class="comment-area"> <h3 class="comment-area_title">提交評論</h3> {% if user.is_authenticated %} <!-- 添加提交評論id --> <form id="comment_form" action="{% url 'update_comment' %}" method="POST" style="overflow:hidden"> {% csrf_token %} <label>{{ user.username }},歡迎評論</label> {% for field in comment_form %} {{ field }} {% endfor %} <!-- 添加錯誤訊息 --> <span id="comment_error" class="text-danger pull-left"></span> <input type="submit" value="評論" class="btn btn-primary pull-right"> </form> {% else %} 您尚未登錄,登錄之後方可評論 <a class="btn btn-primary" href="{% url 'login' %}?from={{ request.get_full_path }}">登錄</a> <a class="btn btn-primary" href="{% url 'register' %}?from={{ request.get_full_path }}">註冊</a> {% endif %} </div> <div class="comment-area"> <h3 class="comment-area_title">評論列表</h3> <!-- 添加評論列表id --> <div id="comment_list"> {% for comment in comments %} <div> {{ comment.user }} ({{ comment.comment_time|date:"Y-m-d H:i:s" }}): {{comment.text |safe }} </div> {% empty %} 暫無評論 {% endfor %} </div> </div> </div> </div>
3.:修改提交評論方法
因為提交對象要改為ajax,所以導入Json回傳的方法
並建立字典來將資料回傳到前端的ajax
由於ajax的時間戳不會根據django setting的TIME_ZONE做調整
因此這邊要導入datetime來調整時間
修改comment應用的<views.py>的update_comment方法:
from django.http import JsonResponse import datetime def update_comment(request): referer = request.META.get('HTTP_REFERER', reverse('update_comment')) comment_form = CommentForm(request.POST,user=request.user)#傳送user參數 data={} if comment_form.is_valid(): #如果檢查通過 comment = Comment() comment.user = comment_form.cleaned_data['user'] comment.text = comment_form.cleaned_data['text'] comment.content_object = comment_form.cleaned_data['content_object'] comment.save() #返回數據 data['status'] = 'SUCCESS' data['username'] = comment.user.username data['comment_time'] = (comment.comment_time + datetime.timedelta(hours=8)).strftime("%Y-%m-%d %H:%M:%S") data['text'] = comment.text else: data['status'] = 'ERROR' #返回表單錯誤訊息 data['message'] = list(comment_form.errors.values())[0] return JsonResponse(data)
4.:撰寫ajax
將ajax撰寫在<blog_detail.html>的js擴充模塊
- 點擊評論
- 判斷是否為空內容
- 提交數據
- 清空編輯框
- 插入數據更新列表
其中因為富文本編輯的關係,需要更新數據到textarea
以下為代碼:
{% block script_extends %} <script type="text/javascript"> $("#comment_form").submit(function(){ //選擇(#為id)comment_form,submit為觸發提交事件,會回調一個function //判斷是否為空 $("#comment_error").text('') if(CKEDITOR.instances['id_text'].document.getBody().getText().trim()==''){ $('#comment_error').text('評論不能為空'); return false; } //更新數據到textarea CKEDITOR.instances['id_text'].updateElement(); //異步提交 $.ajax({ url: "{% url 'update_comment' %}", //提交資料的目的路徑 type: 'POST',//提交資料的類型 data: $(this).serialize(), //序列化表單值 cache: false, //提交網址不須緩存,因此關閉緩存 success: function(data){ console.log(data); if(data['status'] == 'SUCCESS'){ //插入數據 var comment_html = '<div>' + data['username'] + '(' + data['comment_time'] + '):' + data['text'] + '</div>'; $("#comment_list").prepend(comment_html); //清空編輯框內容 CKEDITOR.instances['id_text'].setData(''); }else{ //顯示錯誤訊息 $('#comment_error').text(data['message']); } }, error: function(xhr){ console.log(xhr); }, }); return false; }); </script> {% endblock %}
在表單text資料中,添加關於錯誤訊息的自訂內容
以下為代碼:
text = forms.CharField(widget=CKEditorWidget(config_name='comment_ckeditor'), error_messages={'required':'評論內容不能為空'})
留言列表