一.文章詳情css
路由url.py:html
url(r'^(?P<username>\w+)/article/(?P<pk>\d+)$', views.article_detail),
點擊我的站點文章title便可關聯到文章詳情路由找到其視圖函數前端
views.py:jquery
def article_detail(request,username,pk): user = models.UserInfo.objects.filter(username=username).first() if not user: return render(request, 'error.html') blog = user.blog category_ret=models.Category.objects.all().filter(blog=blog).annotate(cou=Count('article__nid')).values_list('title','cou','nid') tag_ret=models.Tag.objects.all().filter(blog=blog).annotate(cou=Count('article__nid')).values_list('title','cou','nid') year_ret=models.Article.objects.all().annotate(month=TruncMonth('create_time')).values('month').annotate(c=Count('nid')).values_list('month','c') article=models.Article.objects.filter(nid=pk).first() # commit_list=article.commit_set.all() 前臺模板渲染也可獲取評論列表 return render(request,'article_detail.html',locals())
ps: 經過有名分組拿到用戶名和文章id,找到該文章對象渲染到article_detail.html (用到了母版base.html)git
base.html:es6
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{ blog.title }}的我的站點</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> <link rel="stylesheet" href="/static/css/{{ blog.theme }}"> <script src="/static/jquery-3.3.1.js"></script> {% block mycss %} {% endblock %} </head> <body> <div class="head"> {{ blog.site_name }} </div> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading">個人標籤</div> <div class="panel-body"> {% for tag in tag_ret %} <p><a href="/{{ user.username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">隨筆分類</h3> </div> <div class="panel-body"> {% for category in category_ret %} <p> <a href="/{{ user.username }}/category/{{ category.2 }}">{{ category.0 }}({{ category.1 }})</a> </p> {% endfor %} </div> </div> <div class="panel panel-success"> <div class="panel-heading"> <h3 class="panel-title">隨筆檔案</h3> </div> <div class="panel-body"> {% for year in year_ret %} <p> <a href="/{{ user.username }}/archive/{{ year.0|date:"Y-m" }}">{{ year.0|date:"Y年m月" }}({{ year.1 }})</a> </p> {% endfor %} </div> </div> </div> <div class="col-md-9"> {% block content %} {% endblock %} </div> </div> </div> </body> </html>
article_detail.html:ajax
{% extends 'base.html' %} {% block mycss %} <link rel="stylesheet" href="/static/css/commoncss.css"> {% endblock %} {% block content %} <div> <h4>{{ article.title }}</h4> <div> {{ article.content|safe }} </div> <div class="clearfix"> <div id="div_digg"> <div class="diggit upanddown"> <span class="diggnum" id="digg_count">{{ article.up_num }}</span> </div> <div class="buryit upanddown"> <span class="burynum" id="bury_count">{{ article.down_num }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips" style="color: red;"></div> </div> </div> <div> {#評論相關#} <div> {#評論列表#} <p>評論列表</p> <ul class="list-group commit_content"> {% for commit in article.commit_set.all %} <li class="list-group-item"> <p> <span>#{{ forloop.counter }} 樓</span> <span>{{ commit.create_time|date:"Y-m-d H:i" }}</span> <span>{{ commit.user.username }}</span> <span class="pull-right reply" username="{{ commit.user.username }}" commit_id="{{ commit.pk }}"><a>回覆</a></span> </p> {% if commit.parent %} <p class="well">@{{ commit.parent.user.username }}----{{ commit.parent.content }}</p> {% endif %} {{ commit.content }} </li> {% endfor %} </ul> </div> <div> {#發表評論#} <p>發表評論</p> <p> 暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"> </p> <p>評論內容</p> <textarea name="" id="id_textarea" cols="80" rows="10"> </textarea> <p> <button class="btn btn-success" id="btn_submit">提交</button> </p> </div> </div> </div> <script> var parent_id = '' //評論相關 $("#btn_submit").click(function () { var content = $("#id_textarea").val() if (parent_id) { //有值,截取前面的@姓名 //indexof 截取 \n的索引位置 var index = content.indexOf('\n') + 1 content = content.slice(index) } //誰對那篇文章評論了什麼內容 $.ajax({ url: '/commit/', type: 'post', data: { article_id: '{{article.pk}}', content: content, csrfmiddlewaretoken: '{{ csrf_token }}', parent_id: parent_id }, success: function (data) { console.log(data) //清除輸入框的數據 $("#id_textarea").val("") if (data.code == 100) { var username = data.username var reply_content = data.reply_content //日後追加內容 if (parent_id) { var parent_name = data.parent_name var s = ` <li class="list-group-item"> <p> <span>${username}</span> </p> <p class="well">@${parent_name}</p> ${reply_content} </li> ` } else { //追加根評論的內容 //es6的字符串替換 var s = ` <li class="list-group-item"> <p> <span>${username}</span> </p> ${reply_content} </li> ` } $(".commit_content").append(s) } } }) }) //回覆相關 $(".reply").click(function () { var username = '@' + $(this).attr('username') + '\n' parent_id = $(this).attr('commit_id') //光標聚焦到該控件上 $("#id_textarea").focus() $("#id_textarea").val(username) }) //點贊點踩相關 $(".upanddown").click(function () { //當前點擊控件有沒有diggit 這個類 var is_up = $(this).hasClass('diggit') //拿到當前點擊控件子控件的span標籤對象 var cu_span = $(this).children('span') //alert(is_up) //誰對哪篇文章點贊或點踩 $.ajax({ url: '/diggit/', type: 'post', data: {article_id: '{{article.pk}}', is_up: is_up, csrfmiddlewaretoken: '{{ csrf_token }}'}, success: function (data) { console.log(data) $("#digg_tips").html(data.msg) if (data.code == 100) { {#cu_span.text(cu_span.text()+1)#} //在當前點擊的div下的span標籤上數字加以 cu_span.text(Number(cu_span.text()) + 1) } } }) }) </script> {% endblock %}
文章詳情展現瞭如下內容: 文章title, 文章content (傳html文件,用safe轉義),點贊點踩,評論列表,評論內容:django
a.點贊點踩:json
<div class="clearfix"> <div id="div_digg"> <div class="diggit upanddown"> <span class="diggnum" id="digg_count">{{ article.up_num }}</span> </div> <div class="buryit upanddown"> <span class="burynum" id="bury_count">{{ article.down_num }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips" style="color: red;"></div> </div> </div>
js:bootstrap
//點贊點踩相關 $(".upanddown").click(function () { //當前點擊控件有沒有diggit 這個類 var is_up = $(this).hasClass('diggit') //拿到當前點擊控件子控件的span標籤對象 var cu_span = $(this).children('span') //alert(is_up) //誰對哪篇文章點贊或點踩 $.ajax({ url: '/diggit/', type: 'post', data: {article_id: '{{article.pk}}', is_up: is_up, csrfmiddlewaretoken: '{{ csrf_token }}'}, success: function (data) { console.log(data) $("#digg_tips").html(data.msg) if (data.code == 100) { {#cu_span.text(cu_span.text()+1)#} //在當前點擊的div下的span標籤上數字加以 cu_span.text(Number(cu_span.text()) + 1) } } })
點贊點踩視圖函數: 開啓事務,將點贊點踩表建立記錄的同時,把文章表的點贊或點踩數也加一
import json from django.db.models import F def diggit(request): response={'code':100,'msg':None} #當前登錄用戶id if request.user.is_authenticated(): user_id=request.user.nid is_up=request.POST.get('is_up') print(type(is_up)) print(is_up) is_up=json.loads(is_up) print(type(is_up)) print(is_up) article_id=request.POST.get('article_id') up_ret=models.UpAndDown.objects.filter(user_id=user_id,article_id=article_id).first() if up_ret: response['code'] = 102 response['msg'] = '您已經點過了' else: #事務性的操做 from django.db import transaction #開啓事務 with transaction.atomic(): models.UpAndDown.objects.create(article_id=article_id,user_id=user_id,is_up=is_up) if is_up: #文章表點贊字段加一 models.Article.objects.filter(pk=article_id).update(up_num=F('up_num')+1) response['msg'] = '點同意功' else: models.Article.objects.filter(pk=article_id).update(down_num=F('down_num')+1) response['msg'] = '點踩成功' else: response['code']=101 response['msg']='請先登錄' return JsonResponse(response,safe=False)
b.評論列表:
<div> {#評論相關#} <div> {#評論列表#} <p>評論列表</p> <ul class="list-group commit_content"> {% for commit in article.commit_set.all %} <li class="list-group-item"> <p> <span>#{{ forloop.counter }} 樓</span> <span>{{ commit.create_time|date:"Y-m-d H:i" }}</span> <span>{{ commit.user.username }}</span> <span class="pull-right reply" username="{{ commit.user.username }}" commit_id="{{ commit.pk }}"><a>回覆</a></span> </p> {% if commit.parent %} <p class="well">@{{ commit.parent.user.username }}----{{ commit.parent.content }}</p> {% endif %} {{ commit.content }} </li> {% endfor %} </ul> </div>
c.發表評論:
<div> {#發表評論#} <p>發表評論</p> <p> 暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"> </p> <p>評論內容</p> <textarea name="" id="id_textarea" cols="80" rows="10"> </textarea> <p> <button class="btn btn-success" id="btn_submit">提交</button> </p> </div>
js:
var parent_id = '' //評論相關 $("#btn_submit").click(function () { var content = $("#id_textarea").val() if (parent_id) { //有值,截取前面的@姓名 //indexof 截取 \n的索引位置 var index = content.indexOf('\n') + 1 content = content.slice(index) } //誰對那篇文章評論了什麼內容 $.ajax({ url: '/commit/', type: 'post', data: { article_id: '{{article.pk}}', content: content, csrfmiddlewaretoken: '{{ csrf_token }}', parent_id: parent_id }, success: function (data) { console.log(data) //清除輸入框的數據 $("#id_textarea").val("") if (data.code == 100) { var username = data.username var reply_content = data.reply_content //日後追加內容 if (parent_id) { var parent_name = data.parent_name var s = ` <li class="list-group-item"> <p> <span>${username}</span> </p> <p class="well">@${parent_name}</p> ${reply_content} </li> ` } else { //追加根評論的內容 //es6的字符串替換 var s = ` <li class="list-group-item"> <p> <span>${username}</span> </p> ${reply_content} </li> ` } $(".commit_content").append(s) } } }) }) //回覆相關 $(".reply").click(function () { var username = '@' + $(this).attr('username') + '\n' parent_id = $(this).attr('commit_id') //光標聚焦到該控件上 $("#id_textarea").focus() $("#id_textarea").val(username) })
評論視圖函數:開啓事務,在評論表添加記錄的同時,也在文章表添加評論數
def commit(request): response = {'code': 100, 'msg': None} # 當前登錄用戶id if request.user.is_authenticated(): user_id = request.user.nid article_id = request.POST.get('article_id') content=request.POST.get('content') parent_id=request.POST.get('parent_id') # 事務性的操做 from django.db import transaction # 開啓事務 with transaction.atomic(): ret=models.Commit.objects.create(article_id=article_id,content=content,user_id=user_id,parent_id=parent_id) models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1) response['username']=ret.user.username response['reply_content']=ret.content if parent_id: response['parent_name'] = ret.parent.user.username response['msg'] = '評論成功' else: response['code'] = 101 response['msg'] = '請先登錄' return JsonResponse(response, safe=False)
二.後臺展現,添加文章
路由url.py:
# 後臺管理首頁 url(r'^backend/', views.home_backend),
視圖函數:
from django.contrib.auth.decorators import login_required @login_required(login_url='/login/') def home_backend(request): #查詢該人的全部文章 ariticle_list=models.Article.objects.filter(blog=request.user.blog) return render(request,'backend/home_backend.html',locals())
前端渲染母版backend_base.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>後臺管理</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> <script src="/static/jquery-3.3.1.js"></script> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script> </head> <body> <div class="head" style="height: 60px;background-color: #2b669a"> <p>後臺管理</p> </div> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="headingOne"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> 文章管理 </a> </h4> </div> <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <a href="/add_article/">添加文章</a>
跳到添加文章路由
</div> </div> <div id="xxx" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <a href="">添加隨筆</a> </div> </div> </div> </div> </div> <div class="col-md-9"> <div> <!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab" data-toggle="tab">文章</a></li> <li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">隨筆</a> </li> <li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">交友</a> </li> <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">相冊</a> </li> </ul> <!-- Tab panes --> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="home"> {% block content %} {% endblock %} </div> <div role="tabpanel" class="tab-pane" id="profile">隨筆</div> <div role="tabpanel" class="tab-pane" id="messages">交友</div> <div role="tabpanel" class="tab-pane" id="settings">相冊</div> </div> </div> </div> </div> </div> </body> </html>
後臺管理前端home_backend.html:
{% extends 'backend/backend_base.html' %} {% block content %} <table class="table table-striped"> <thead> <tr> <th>標題</th> <th>評論數</th> <th>點贊數</th> <th>操做</th> <th>操做</th> </tr> </thead> <tbody> {% for ariticle in ariticle_list %} <tr> <td><a href="/{{ request.user.username }}/article/{{ ariticle.pk }}">{{ ariticle.title }}</a></td>
跳到文章詳情路由 <td>{{ ariticle.commit_num }}</td> <td>{{ ariticle.up_num }}</td> <td><a href="">刪除</a></td> <td><a href="">編輯</a></td> </tr> {% endfor %} </tbody> </table> {% endblock %}
添加文章路由:
# 添加文章 url(r'^add_article/', views.add_article),
添加文章視圖函數:(用bs4來解析html文檔,刪除script標籤內容來防止xss
@login_required(login_url='/login/') def add_article(request): if request.method=='GET': return render(request,'backend/add_article.html') else: title=request.POST.get('title') text_content=request.POST.get('text_content') #經過bs4 處理xss攻擊,html文檔解析庫 #pip3 install beautifulsoup4 from bs4 import BeautifulSoup soup=BeautifulSoup(text_content,'html.parser') #查找全部的標籤 tags=soup.find_all() for tag in tags: if tag.name=='script': #從文檔中刪除該標籤 tag.decompose() #soup.text 文檔的內容,不包含標籤 desc=soup.text[0:150] models.Article.objects.create(title=title,desc=desc,content=str(soup),blog=request.user.blog) return redirect('/backend/')
添加文章add_article.html(藉助富文本編輯器):
{% extends 'backend/backend_base.html' %} {% block content %} <div> <p>添加文章</p> <form action="/add_article/" method="post"> {% csrf_token %} <div class="form-group"> <label for="id_name">文章標題</label> <input type="text" name="title" id="id_title" class="form-control"> </div> <p>內容(KindEditor編輯器,不支持拖放/粘貼上傳圖片) </p> <textarea name="text_content" id="editor_id" cols="30" rows="10"></textarea> <p> <button class="btn btn-success">提交</button> </p> </form> </div> <script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script> <script> KindEditor.ready(function (K) { window.editor = K.create('#editor_id', { width: '100%', height: '400px', resizeType: 0, {#items: [#} {# 'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',#} {# 'plainpaste', 'wordpaste', '|'#} {#]#} uploadJson: '/uploadimg/', extraFileUploadParams: { csrfmiddlewaretoken: '{{ csrf_token }}', } }); }); </script> {% endblock %}
在富文本編輯器中上傳圖片,圖片地址放在media/file下便可:
@login_required(login_url='/login/') def uploadimg(request): response={ "error" : 0, "url" : None } fil=request.FILES.get('imgFile') with open('media/file/'+fil.name,'wb') as f: for line in fil: f.write(line) response['url']='/media/file/'+fil.name return JsonResponse(response)