Django LightBlog 博客

LightBlog 博客

LightBlog博客系統(tǒng)是我用了近四兩個月寫的一個基于Django的博客系統(tǒng)(這也是很感謝簡書的一位dalao,很多啟發(fā)都是他的博客那的??梢匀タ纯?a href="http://www.itdecent.cn/p/ce4384ea5e06" target="_blank">http://www.itdecent.cn/p/ce4384ea5e06)?;貧w正題,本博客基本的功能也是正常博客所擁有的,如用戶方面有登錄注冊,修改密碼,個人的首頁,個人信息修改,頭像剪切上傳,個人博客頁面,個人博客的編寫(基于markdown)。博客方面:用于可以為自己寫的博客加欄目分類,博客上傳后也是展示在首頁,博客也是可以在線修改,有博客點贊功能和簡單評論功能等等。前端頁面是用一個很小的前端框架搭建——layui。由于該博客有好的方面也有帶改進的方面,所以這里就不多一一闡述,該這篇博客也是會一直置頂,更改。歡迎來踩踩(別踩壞了)——LightBlog博客

個人寫博客一般都是hexo先寫的,不怎么喜歡簡書寫,這次寫也是紀念一下,很多的功能部分看著篇博客一般是解決不了的,可以看我的自己的博客——LightBlog,或者放在gitee上的 https://lightfisher.gitee.io/blog .這個項目也是放在在我的github上: https://github.com/QGtiger/LightBlog

1.評論動態(tài)添加和刪除

在評論的時候我原本是發(fā)出評論就前端界面刷新已完成加載,這次也是無聊進行了優(yōu)化,用ajax動態(tài)加載

1.首頁是后端代碼的實現(xiàn),至于路由的 選擇這里就不加闡述了,Just show code

from django.shortcuts import render,get_object_or_404,HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .models import ArticlePost,Comment
from django.conf import settings
import json


@csrf_exempt
def article_content(request, article_id):
    article = get_object_or_404(ArticlePost, id=article_id)
    if request.method == 'POST':
        comment = request.POST.get('comment','')
        user = request.user
        try:
            C = Comment(article=article,commentator=user,body=comment)
            C.save()
            comment_info = {'commentator':user.username,'id': C.id, 'body': C.body, 'created': C.created.strftime("%Y-%m-%d %H:%M:%S")}
            return HttpResponse(json.dumps({"static":200,"tips":"感謝您的評論", 'comment_info':comment_info}))
        except:
            return HttpResponse(json.dumps({"static":500,"tips":"評論系統(tǒng)出現(xiàn)錯誤"}))
    else:
        total_views = r.incr("article:{}:views".format(article_id))
        r.zincrby('article_ranking', 1, article_id)
        comments = Comment.objects.filter(article=article)
        return render(request, "article/article_content.html", locals())

上述的視圖函數(shù)也是接收數(shù)據(jù)后創(chuàng)建數(shù)據(jù)庫對象,然后后端處理數(shù)據(jù)向前端傳送數(shù)據(jù)已構造前端界面。前端接收數(shù)據(jù)和發(fā)送數(shù)據(jù)的函數(shù)如下

$('.comment-btn').on('click',function(){
    if($('.username').val()==''){
        layer.msg('請先登錄')
    }else{
        var comment = $('.comment_text').val();
        $.ajax({
            url: '{% url "article:article_content" article.id %}',
            type: "POST",
            data: {'comment':comment },
            success: function(e){
                $('.comment_text').val('');
                res = JSON.parse(e);
                if(res.static='200'){
                    console.log(res);
                    commentator = res.comment_info;
                    prepend_li='<li onmouseenter="EnterFunction(this)" onmouseleave="LeaveFunction(this)">'+
                '<div class="commentator">'+
                    '<div class="commentator-img">'+
                        '<a href="/account/author/'+commentator.commentator+'">'+
                            '<img src="/media/avator/'+commentator.commentator+'.jpg" class="layui-nav-img" alt="">'+
                        '</a>'+
                    '</div>'+
                    '<div class="commentator-info">'+
                        '<a href="/account/author/'+commentator.commentator+'">'+commentator.commentator+'</a>'+
                        '<span style="margin-left:20px">'+commentator.created+'</span>'+
                        '<span style="margin-left:20px"> 人氣值: <span class="support-comment">0</span></span>'+
                    '</div>'+
                    '<div class="comment-wrap">'+
                        '<p>'+commentator.body+'</p>'+
                    '</div>'+
                    '<div class="meta">'+
                        '<span onclick="comment_like('+commentator.id+',\'like\',this)"><span><i class="layui-icon layui-icon-praise"></i> <span class="comment-like">贊</span></span></span>'+
                        '<span onclick="comment_like('+commentator.id+',\'unlike\',this)"><span><i class="layui-icon layui-icon-tread"></i> <span class="comment-unlike">踩</span></span></span>'+
                        '<a href="javascript:void(0)" onclick="comment_delete('+ commentator.id +', this)" class="comment-tool comment-delete">刪 除</a>'+
                    '</div>'+
                '</div>'+
            '</li>'

                    $('.history-comment ul').prepend(prepend_li);
                }else{
                    layer.msg(res.tips);
                }
            }
        })
    }
})

其實這件那一大串js代碼只是構造評論標簽,好動態(tài)加載上去,中間幾個函數(shù)你可以不用管,你主要的是知道這前后端結合的工作流程~~~(別問,問我也沒法告訴你,我不會告訴你aboutme 有點聯(lián)系方式)

流程也是蠻簡單的,簽單單擊發(fā)送,觸發(fā)js函數(shù),向后端發(fā)送請求和數(shù)據(jù),后端接收,然后進行驗證和數(shù)據(jù)庫的操作,然后向前端發(fā)送response,前端接收和進行響應。是不是很簡單呀~~

評論刪除

眼尖的應該就出來了,評論刪除的功能也是類似,這里也是對用戶交互之間做了優(yōu)化,如,鼠標移動到之間的評論是刪除,也別人的評論是舉報。實現(xiàn)前端代碼如下:

function EnterFunction(e){
    $(e).find('.comment-tool').css('display','block');
} //移動到評論內就顯示

function LeaveFunction(e){
    $(e).find('.comment-tool').css('display','none');
} //移動出就隱藏

//點擊事件
function comment_delete(id, e){
    if($('.username').val()==''){
        layer.msg('請先登錄')
    }else{
        layer.confirm('確定要刪除該評論?',{
            btn: ['確定', '取消']
        },function(){
            $.ajax({
                url: "{% url 'article:comment_delete' %}",
                method: "POST",
                data: {'id':id},
                dataType: 'json',
                success: function(res){
                    if(res.static=='201'){
                        dom = $(e).parent().parent().parent();
                        dom.remove();
                        layer.msg(res.tips,{icon:1})
                    }else if(res.static=='502'){
                        layer.msg(res.tips,{icon:2})
                    }else{
                        layer.msg(res.tips)
                    }
                }
            })
        })
    }
}

后端代碼

@csrf_exempt
@require_POST
def comment_delete(request):
    comment_id = request.POST['id']
    comment = Comment.objects.get(id=comment_id)
    try:
        if(request.user == comment.commentator):
            comment.delete()
            return HttpResponse(json.dumps({'static':201, 'tips':'評論已刪除'}))
        else:
            return HttpResponse(json.dumps({'static':502, 'tips':"You don't have permission.."}))
    except:
        return HttpResponse(json.dumps({'static':500, 'tips':'Something Error...'}))

當當,這就是這是實現(xiàn)的代碼系不系很簡單,主要是思路,嘻嘻,下面是實現(xiàn)的效果

img

Redis 簡單實現(xiàn)文章瀏覽次數(shù)

關于redis的基本語法,我上幾篇也是簡單介紹過了。這次也算是一次簡單的運用

1.首先在settings.py文件中做好數(shù)據(jù)庫的配置

#redis配置
REDIS_HOST = '127.0.0.1'
REDIS_PORT = '6379'
REDIS_DB = 0

2.在視圖函數(shù)中和redis建立連接

import redis

r = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT)

3.修改文章頁面的視圖函數(shù),只需要看GET請求,POST請求是評論

@csrf_exempt
def article_content(request, article_id):
    article = get_object_or_404(ArticlePost, id=article_id)
    if request.method == 'POST':
        comment = request.POST.get('comment','')
        user = request.user
        try:
            C = Comment(article=article,commentator=user,body=comment)
            C.save()
            return HttpResponse(json.dumps({"static":200,"tips":"感謝您的評論"}))
        except:
            return HttpResponse(json.dumps({"static":500,"tips":"評論系統(tǒng)出現(xiàn)錯誤"}))
    else:
        total_views = r.incr("article:{}:views".format(article_id))
        r.zincrby('article_ranking', 1, article_id)
        comments = Comment.objects.filter(article=article)
        return render(request, "article/article_content.html", locals())

4.然后在相應的模板文件內添加

<span style="margin-left:20px">{{ total_views }} view{{ total_views|pluralize }}</span>

5.效果如下:

img

redis 簡單實現(xiàn)博客的最受歡迎文章

修改博客首頁的視圖函數(shù)

def article_titles(request):
    length = r.zcard('article_ranking')
    article_ranking = r.zrange("article_ranking", 0, length, desc=True)[:5]
    article_ranking_ids = [int(id) for id in article_ranking]
    most_viewed = list(ArticlePost.objects.filter(id__in=article_ranking_ids))
    most_viewed.sort(key=lambda x: article_ranking_ids.index(x.id))
    return render(request,'article/article_titles.html',locals())

然后在模板內增加相應的展示代碼

<div class="article_ranking" style="margin:20px 40px">
        <h3>最受歡迎文章</h3>
        <ol>
            {% for article in most_viewed %}
            <li><a href="{% url 'article:article_content' article.id %}">{{ forloop.counter }}. {{ article.title }}</a></li>
            {% endfor %}
        </ol>
    </div>

實現(xiàn)效果

img

對博客評論的評論

1.首先是在models.py里編寫數(shù)據(jù)庫代碼,結構如下

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
# 在article app下的models里引用相對應的model
from article.models import Comment, ArticlePost


class Comment_reply(models.Model):
    comment_reply = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name="comment_reply") # 回復的主評論
    reply_type = models.IntegerField(default=0) # 回復的類型,0為主評論,1為評論下的評論
    comment_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="commentator_reply") # 回復人
    commented_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="commented_user", blank=True, null=True)# 被回復的人
    created = models.DateTimeField(default=timezone.now)
    body = models.TextField()
    reply_comment = models.IntegerField(default=0) # 回復那條評論,id
    is_read = models.IntegerField(default=0) # 是否查看,為了應對后面的消息系統(tǒng)

    class Meta:
        ordering = ('-created',)

    def __str__(self):
        return "Comment by {} on {}".format(self.comment_user, self.created)

2.記得編寫完了后生成數(shù)據(jù)庫表,python manage.py makemigrations , python manage.py migrate

3.然后編寫前端代碼,向后端動態(tài)發(fā)送數(shù)據(jù)

$('body').on('click', '.comment-reply-btn', function(e){
        if($('.username').val()==''){
            layer.msg('請先登錄')
        }else{
            var dom = $(this).parent().parent().parent();
            if(dom.parent().hasClass('commentator-root')){
                data = {'reply_type': 0,'id': dom.attr('comment_id'), 'body': dom.children("#textarea").val()}
            }else if(dom.parent().hasClass('commentator-child')){
                data = {'reply_type': 1, 'comment_id': dom.parent().parent().attr('comment_id'), 'id':dom.attr('comment_id'), 'body': dom.children("#textarea").val()}
            }else{
                layer.msg('別搞我呀..');
                return;
            }
            console.log(data)
            $.ajax({
                url: "{% url 'comment:comment_reply' %}",
                method: "POST",
                dataType: "json",
                data: data,
                success: function(res){
                    if(res.code=='203'){
                        console.log(res.res)
                        layer.msg(res.tips);
                        dom.html('')
                        //console.log(dom)
                        dom.prev().find(".tool-reply").text('回 復')
                        data = res.res
                        item = '<div class="commentator commentator-child" id="comment-child-'+data.id+'" onmouseenter="EnterFunction(this)" onmouseleave="LeaveFunction(this)">'+
                        '<div class="commentator-img">'+
                            '<a href="/account/author/'+data.from+'">'+
                                '<img src="/media/avator/'+data.from+'.jpg" class="layui-nav-img" alt="">'+
                            '</a>'+
                        '</div>'+
                        '<div class="commentator-info">'+
                            '<a href="/account/author/'+data.from+'" class="commentator-name">'+data.from+'</a>'+
                            '<span style="margin:0 10px">回復</span>'+
                            '<a href="/account/author/'+data.to+'">'+data.to+'</a>'+
                            '<span style="margin-left:20px">'+ data.created+'</span>'+
                        '</div>'+
                        '<div class="comment-wrap">'+
                            '<pre>'+data.body+'</pre>'+
                        '</div>'+
                        '<div class="meta">'+
                            '<a href="javascript:void(0)" onclick="comment_reply_delete('+data.id+', this)" class="comment-tool comment-delete"><span><i class="layui-icon layui-icon-delete"></i> <span>刪 除</span></span></a>'+
                        '</div>'+
                        '<div class="reply_input" comment_id="'+data.id+'"></div>'+
                    '</div>'
                        dom.parent().parent().find('.commentator-root').after($(item))
                        is_reply = 0;
                    }else{
                        layer.msg(res.tips);
                    }
                }
            })
        }

    })

4.以上代碼就是判斷是否是主評論,來改變向后端傳輸?shù)臄?shù)據(jù)。后端編寫路由和視圖函數(shù),視圖函數(shù)如下,路由就不展示了

from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
from article.models import Comment
from .models import Comment_reply
import json

@csrf_exempt
@require_POST
def comment_reply(request):
    reply_type = request.POST.get('reply_type','')
    if reply_type == '0':
        id = request.POST.get('id','')
        body = request.POST.get('body','')
        if body.strip() == '':
            return HttpResponse(json.dumps({'code':201,'tips':'內容不能為空'}))
        else:
            try:
                comment = Comment.objects.get(id=id)
                user = request.user
                if user == comment.commentator:
                    return HttpResponse(json.dumps({'code':202,'tips':'別搞我'}))
                Com = Comment_reply(comment_reply=comment, comment_user=user, commented_user=comment.commentator, body=body)
                Com.save()
                comment_info = {'from': user.username,'to':comment.commentator.username , 'id': Com.id, 'body': Com.body,
                                'created': Com.created.strftime("%Y-%m-%d %H:%M:%S")}
                return HttpResponse(json.dumps({'code':203, 'tips':'評論成功', 'res':comment_info}))
            except:
                return HttpResponse(json.dumps({"code": 501, "tips": "評論系統(tǒng)出現(xiàn)錯誤"}))
    else:
        comment_id = request.POST.get('comment_id','')
        id = request.POST.get('id', '')
        body = request.POST.get('body', '')
        if body.strip() == '':
            return HttpResponse(json.dumps({'code':201,'tips':'內容不能為空'}))
        else:
            try:
                comment = Comment.objects.get(id=comment_id)
                comment_reply = Comment_reply.objects.get(id=id)
                user = request.user
                if user == comment_reply.comment_user:
                    return HttpResponse(json.dumps({'code':202,'tips':'別搞我'}))
                Com = Comment_reply(comment_reply=comment, reply_type=1, comment_user=user, reply_comment=id, commented_user=comment_reply.comment_user, body=body)
                Com.save()
                comment_info = {'from': user.username, 'to': comment_reply.comment_user.username, 'id': Com.id, 'body': Com.body,
                                'created': Com.created.strftime("%Y-%m-%d %H:%M:%S")}
                return HttpResponse(json.dumps({'code': 203, 'tips': '評論成功', 'res': comment_info}))
            except:
                return HttpResponse(json.dumps({"code": 501, "tips": "評論系統(tǒng)出現(xiàn)錯誤"}))

以上就是Django后端的處理函數(shù),可能有些地方并不是很好,后期再改進。后端處理完數(shù)據(jù)并數(shù)據(jù)庫保存好后,返回數(shù)據(jù),前端接收到后,也是ajax動態(tài)加載上去。

這里主要也是簡單的前后端結合實現(xiàn)動態(tài)加載,難的是后端的數(shù)據(jù)庫邏輯,So,show you the pic。

img

個人界面的優(yōu)化

這次也是對個人界面的簡單優(yōu)化,如可以看自己發(fā)布的文章,也可以看作者點贊的文章,后面有空也是可以添加用戶動態(tài),如,評論或者留言板

首先這里使用的是layui框架的選項卡組件,在相關的html模板文件添加

<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief" style="margin: 0 5%">
    <ul class="layui-tab-title">
        <li class="layui-this">個人文章</li>
        <li>點贊文章</li>
    </ul>
    <div class="layui-tab-content" style="height: 100px;">
        <ul class="layui-tab-item layui-show" id="flow_myself"></ul>
        <ul class="layui-tab-item" id="flow_article"></ul>
    </div>
</div>

然后下面就是運用layui框架的流加載

<script type="text/javascript">
    layui.use(['layer','flow','element'],function(){
        var layer = layui.layer;
        var flow = layui.flow;
        var element = layui.element;

        flow.load({
            elem: "#flow_myself",
            is_Auto:true,
            end: "emm,nothing next",
            is_Lazyimg: true,
            done: function(page,next){
                var lis = '';
                $.get('/account/article_page/{{user.username}}?page='+page, function(e){
                    parse_res = JSON.parse(e)
                    res = parse_res.data
                    page_num = parse_res.page_num
                    console.log(res)
                    for(var i = 0; i < res.length; i++){

                        var item = '<!-- 此處省略動態(tài)加載html代碼 -->'

                        lis+=item;

                    }
                    next(lis,page<page_num)
                })


            }
        })

        flow.load({
            elem: "#flow_article",
            is_Auto:true,
            end: "emm,nothing next",
            is_Lazyimg: true,
            done: function(page,next){
                var lis = '';
                $.get('/account/article_like/{{ user.username }}?page='+page, function(e){
                    parse_res = JSON.parse(e)
                    res = parse_res.data
                    page_num = parse_res.page_num
                    console.log(res)
                    for(var i = 0; i < res.length; i++){

                        var item = '<!-- 此處省略動態(tài)加載html代碼 -->'

                        lis+=item;

                    }
                    next(lis,page<page_num)
                })


            }
        })
    })
</script>

關于流加載layui框架也是有詳細介紹,我之前的文章也是有過簡單介紹。然后就是django的后端處理返回數(shù)據(jù),前端接收動態(tài)加載html頁面了,這里的話,我就只介紹用戶點贊文章的后臺返回數(shù)據(jù)的視圖函數(shù)

from django.core.paginator import PageNotAnInteger,Paginator,EmptyPage
import json

def article_like(request, username):
    user = User.objects.get(username=username)
    articles = user.users_like.all() #反向查詢點贊文章
    paginator = Paginator(articles, 6) # 分頁
    page = request.GET.get('page')
    try:
        current_page = paginator.page(page)
        article_list = current_page.object_list
    except PageNotAnInteger:
        current_page = paginator.page(1)
        article_list = current_page.object_list
    except EmptyPage:
        current_page = paginator.page(paginator.num_pages)
        article_list = current_page.object_list
    articles_json = []
    for i in range(len(article_list)):
        articles_json.append({'id': article_list[i].id, 'title': article_list[i].title,
                              'updated': article_list[i].updated.strftime("%Y-%m-%d %H:%M:%S"),
                              'author': article_list[i].author.username,
                              'body': article_list[i].body[:70], 'users_like': article_list[i].users_like.count()})
    return HttpResponse(json.dumps({'code':201, 'data':articles_json, 'page_num':paginator.num_pages}))

這里就是簡單的處理數(shù)據(jù)并返回數(shù)據(jù)構成前端頁面,這里的話,可能關系到models文件的數(shù)據(jù)庫對象,這里也是不展示了,你可以去我的githup上git clone下來看看。地址

下面就是簡單的實現(xiàn)圖

img

發(fā)布時間的優(yōu)化

效果圖如下

img

實現(xiàn)就很簡單了,js代碼如下,后端返回數(shù)據(jù)時間戳,前端接收時間戳并進行轉化,這里也是我后端轉化為時間戳遇到的問題,數(shù)據(jù)庫里是datetime類型,你就需要使用time.mktime(datetime.timetuple()),這里的datetime就是要轉換時間類型

var Time = {
    // 獲取當前時間戳
    getUnix: function(){
        var date = new Date();
        return date.getTime();
    },
    // 獲取今天0:0的時間戳
    getTodayUnix: function(){
        var date = new Date();
        date.setHours(0);
        date.setMilliseconds(0);
        date.setMinutes(0);
        date.setSeconds(0);
        return date.getTime();
    },
    // 獲取今年的1月1號0時0分的時間戳
    getYearUnix: function(){
        var date = new Date();
        date.setHours(0);
        date.setMilliseconds(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMonth(0);
        date.setDate(1);
        return date.getTime();
    },
    //獲取標準年月日
    getLastDate: function(timestamp){
        var date = new Date(timestamp);
        var month = date.getMonth() + 1<10?'0'+(date.getMonth() + 1):date.getMonth() + 1;
        var day = date.getDate() <10?'0'+date.getDate():date.getDate();
        return date.getFullYear() + "-"+month+"-"+day;
    },
    //轉換時間
    getFormatTime: function(time){
        var timestamp = time * 1000;
        var now = this.getUnix();
        var today = this.getTodayUnix();
        var year = this.getYearUnix();
        var timer = (now-timestamp)/1000;
        var tips = '';
        if(Math.floor(timer)<60){
            tips = '剛剛';
        }else if(timer<3600){
            tips = Math.floor(timer/60)+'分鐘前';
        }else if(timer>=3600 && (timestamp-today>=0)){
            tips = Math.floor(timer/3600)+'小時前';
        }else if(timer/86400<=31){
            tips = Math.ceil(timer/86400)+'天前';
        }else{
            tips = this.getLastDate(timestamp);
        }
        return tips;
    }
}




其實主要還是redis相應的函數(shù)需要理解,So

Just have fun..

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容