python 全棧開發,Day83(博客系統子評論,後臺管理,富文本編輯器kindeditor,bs4模塊)

1、子評論

必須點擊回覆,纔是子評論!不然是根評論
點擊回覆以後,定位到輸入框,同時加入@評論者的用戶名javascript

定位輸入框

focus

focus:獲取對象焦點觸發事件php

先作樣式。點擊回覆以後,定位到輸入框,加入被評論的用戶名css

給回覆的a標籤加一個class=reply_btn,關閉a標籤的跳轉,使用javascript:void(0)html

修改article_detail.html,增長一段回覆的jsjava

{% extends "base.html" %}

{% block content %}
    <div class="article_info">
        <h4 class="text-center">{{ article_obj.title }}</h4>
        <div class="content">
            {{ article_obj.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
        <div class="clearfix"></div>

        <div class="comment">
            <p>評論列表</p>
            <ul class="comment_list list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}樓</a>&nbsp;&nbsp;
                            <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                            <a href="">{{ comment.user.username }}</a>
                            <a href="javascript:void(0)" class="pull-right reply_btn"><span>回覆</span></a>

                        </div>
                        <div>
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
                {% endfor %}

            </ul>
            <p>發表評論</p>
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <div>
                <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            </div>
            <input type="button" value="submit" class="btn btn-default comment_btn">
        </div>

    </div>
    {% csrf_token %}
    <script>
        // 點贊和踩滅
        $(".action").click(function () {
            {#hasClass() 方法檢查被選元素是否包含指定的 class#}
            var is_up = $(this).hasClass("diggit");
            {#獲取提示的span標籤#}
            var _this = $(this).children("span");
            {#判斷是否登陸#}
            if ("{{ request.user.username }}") {
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        is_up: is_up,
                        article_id: "{{ article_obj.pk }}",
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        console.log(typeof data);
                        if (data.state) {
                            //提交成功
                            var val = _this.text();  //獲取text值
                            //在原有的基礎上加1。注意:必定要進行類型轉換
                            _this.text(parseInt(val) + 1)
                        } else {
                            // 重複提交
                            var val = data.handled ? "您已經推薦過!" : "您已經反對過!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")  //清空提示文字
                            }, 1000)
                        }

                    }
                })
            } else {
                location.href = "/login/";
            }

        })

        // 提交評論
        $(".comment_btn").click(function () {
            {#評論內容#}
            var content = $("#comment_content").val();
            {#默認爲空#}
            var pid = "";
            $.ajax({
                url: "/comment/",
                type: "post",
                data: {
                    content: content,
                    article_id: "{{ article_obj.pk }}",
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    console.log(data);
                    {#獲取3個值#}
                    var comment_time = data.timer;
                    var comment_content = data.content;
                    var comment_user = data.user;
                    {#組織li標籤#}
                    var $li = ` <li class="list-group-item">
                                       <div>
                                           <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                           <a href="">${comment_user}</a>
                                       </div>
                                       <div>
                                           <p>${comment_content}</p>
                                       </div>
                                    </li>`;
                    {#追加到評論列表中#}
                    $(".comment_list").append($li);
                    // 清空輸入框的內容
                    $("#comment_content").val("")
                }
            })

        })

        // 回覆按鈕事件
        $(".reply_btn").click(function () {
            {#獲取到textarea輸入框光標#}
            $("#comment_content").focus();


        })
    </script>

{% endblock %}
View Code

換一個zhang用戶登陸,訪問一篇有回覆的博客,點擊回覆,效果以下:python

它會自動定位到輸入框的位置jquery

 

增長@評論用戶

注意:這裏的評論用戶是這一條評論的用戶,好比上面的xiaogit

那麼js如何獲取評論人的用戶名呢?使用render渲染就能夠了!github

給回覆的a標籤添加一個自定義屬性usernameajax

$this能直接獲取這個a標籤。

修改article_detail.html

{% extends "base.html" %}

{% block content %}
    <div class="article_info">
        <h4 class="text-center">{{ article_obj.title }}</h4>
        <div class="content">
            {{ article_obj.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
        <div class="clearfix"></div>

        <div class="comment">
            <p>評論列表</p>
            <ul class="comment_list list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}樓</a>&nbsp;&nbsp;
                            <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                            <a href="">{{ comment.user.username }}</a>
                            <a href="javascript:void(0)" class="pull-right reply_btn"
                               username="{{ comment.user.username }}"><span>回覆</span></a>

                        </div>
                        <div>
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
                {% endfor %}

            </ul>
            <p>發表評論</p>
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <div>
                <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            </div>
            <input type="button" value="submit" class="btn btn-default comment_btn">
        </div>

    </div>
    {% csrf_token %}
    <script>
        // 點贊和踩滅
        $(".action").click(function () {
            {#hasClass() 方法檢查被選元素是否包含指定的 class#}
            var is_up = $(this).hasClass("diggit");
            {#獲取提示的span標籤#}
            var _this = $(this).children("span");
            {#判斷是否登陸#}
            if ("{{ request.user.username }}") {
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        is_up: is_up,
                        article_id: "{{ article_obj.pk }}",
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        console.log(typeof data);
                        if (data.state) {
                            //提交成功
                            var val = _this.text();  //獲取text值
                            //在原有的基礎上加1。注意:必定要進行類型轉換
                            _this.text(parseInt(val) + 1)
                        } else {
                            // 重複提交
                            var val = data.handled ? "您已經推薦過!" : "您已經反對過!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")  //清空提示文字
                            }, 1000)
                        }

                    }
                })
            } else {
                location.href = "/login/";
            }

        })

        // 提交評論
        $(".comment_btn").click(function () {
            {#評論內容#}
            var content = $("#comment_content").val();
            {#默認爲空#}
            var pid = "";
            $.ajax({
                url: "/comment/",
                type: "post",
                data: {
                    content: content,
                    article_id: "{{ article_obj.pk }}",
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    console.log(data);
                    {#獲取3個值#}
                    var comment_time = data.timer;
                    var comment_content = data.content;
                    var comment_user = data.user;
                    {#組織li標籤#}
                    var $li = ` <li class="list-group-item">
                                       <div>
                                           <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                           <a href="">${comment_user}</a>
                                       </div>
                                       <div>
                                           <p>${comment_content}</p>
                                       </div>
                                    </li>`;
                    {#追加到評論列表中#}
                    $(".comment_list").append($li);
                    // 清空輸入框的內容
                    $("#comment_content").val("")
                }
            })

        })

        // 回覆按鈕事件
        $(".reply_btn").click(function () {
            {#獲取到textarea輸入框光標#}
            $("#comment_content").focus();
            {#增長@+用戶名+換行符#}
            var val = "@" + $(this).attr("username") + "\n";
            {#修改輸入框的值#}
            $("#comment_content").val(val);
            
        })
    </script>

{% endblock %}
View Code

從新刷新頁面,並點擊回覆,效果以下:

首頁增長鏈接

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/js/jquery.js"></script>
    <script src="/static/bootstrap/js/bootstrap.js"></script>
    <style>
        .desc {
            text-align: justify;
        }

        .info {
            margin-top: 10px;
        }

        h5 a {
            color: #105cb6;
            font-size: 14px;
            font-weight: bold;
            text-decoration: underline;
        }

        .diggit {
            float: left;
            margin-right: 20px;
            width: 46px;
            height: 52px;
            background-image: url('/static/img/upup.gif');
        }

        .diggnum {
            position: relative;
            top: 6px;
            left: 20px;
        }

    </style>
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">博客園</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">新聞 <span class="sr-only">(current)</span></a></li>
                <li><a href="#">博問</a></li>

            </ul>

            <ul class="nav navbar-nav navbar-right">
                {% if request.user.username %}
                    <li><a href="#"><span class="glyphicon glyphicon-user"></span>&nbsp;{{ request.user.username }}</a>
                    </li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">Dropdown <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">修改密碼</a></li>
                            <li><a href="#">我的信息</a></li>
                            <li><a href="/logout/">註銷</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</a></li>
                        </ul>
                    </li>

                {% else %}
                    <li><a href="/login/">登錄</a></li>
                    <li><a href="#">註冊</a></li>
                {% endif %}


            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>

            <div class="panel panel-danger">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
        </div>
        <div class="col-md-6">

            <div class="article_list">
                {% for article in article_list %}
                    <div class="diggit">
                        <span class="diggnum">0</span>
                    </div>
                    <div class="article_item">

                        <h5><a href="/{{ article.user.username }}/articles/{{ article.pk }}">{{ article.title }}</a></h5>
                        <div>
                            <span class="media-left"><a href="/{{ article.user.username }}"><img width="48" height="48"
                                    src="https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1758343206,1224786249&fm=58&bpow=1024&bpoh=1536"
                                    alt=""></a></span>

                            <span class="media-right small desc ">
                              {{ article.desc }}
                          </span>

                        </div>
                        <div class="info small">
                            <span><a href="/{{ article.user.username }}">{{ article.user.username }}</a></span> &nbsp;
                            發佈於 <span>{{ article.create_time|date:'Y-m-d H:i' }}</span>&nbsp;&nbsp;
                            <img src="/static/img/icon_comment.gif" alt=""><a
                                href="/{{ article.user.username }}/articles/{{ article.pk }}">評論({{ article.comment_count }})</a>&nbsp;&nbsp;
                            <span class="glyphicon glyphicon-thumbs-up"></span><a href="/{{ article.user.username }}/articles/{{ article.pk }}">點贊({{ article.up_count }})</a>
                        </div>
                    </div>
                    <hr>
                {% endfor %}
            </div>


        </div>
        <div class="col-md-3">

            <div class="panel panel-warning">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
            <div class="panel panel-success">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>

        </div>
    </div>
</div>

</body>
</html>
View Code

效果以下:

點擊文章標題,能夠進入文章詳情頁。

 

提交子評論

若是是下面這種狀態,直接回復後,就是一個根評論

爲何呢?由於前臺和後端,沒有作判斷!

ajax提交數據時,pid的變量不該該爲空,應該發送一個父評論id才行!

何時,對pid賦值呢?
正確作法是:點擊回覆時,將pid賦值。
怎麼拿到父評論的id呢?使用render渲染,給a標籤加一個子定義屬性comment_id
而後利用js獲取,進行賦值便可

獲取父評論id,並賦值

修改article_detail.html

{% extends "base.html" %}

{% block content %}
    <div class="article_info">
        <h4 class="text-center">{{ article_obj.title }}</h4>
        <div class="content">
            {{ article_obj.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
        <div class="clearfix"></div>

        <div class="comment">
            <p>評論列表</p>
            <ul class="comment_list list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}樓</a>&nbsp;&nbsp;
                            <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                            <a href="">{{ comment.user.username }}</a>
                            <a href="javascript:void(0)" class="pull-right reply_btn"
                               username="{{ comment.user.username }}" comment_id="{{ comment.pk }}"><span>回覆</span></a>

                        </div>
                        <div>
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
                {% endfor %}

            </ul>
            <p>發表評論</p>
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <div>
                <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            </div>
            <input type="button" value="submit" class="btn btn-default comment_btn">
        </div>

    </div>
    {% csrf_token %}
    <script>
        // 點贊和踩滅
        $(".action").click(function () {
            {#hasClass() 方法檢查被選元素是否包含指定的 class#}
            var is_up = $(this).hasClass("diggit");
            {#獲取提示的span標籤#}
            var _this = $(this).children("span");
            {#判斷是否登陸#}
            if ("{{ request.user.username }}") {
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        is_up: is_up,
                        article_id: "{{ article_obj.pk }}",
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        console.log(typeof data);
                        if (data.state) {
                            //提交成功
                            var val = _this.text();  //獲取text值
                            //在原有的基礎上加1。注意:必定要進行類型轉換
                            _this.text(parseInt(val) + 1)
                        } else {
                            // 重複提交
                            var val = data.handled ? "您已經推薦過!" : "您已經反對過!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")  //清空提示文字
                            }, 1000)
                        }

                    }
                })
            } else {
                location.href = "/login/";
            }

        })

        // 提交評論
        $(".comment_btn").click(function () {
            {#評論內容#}
            var content = $("#comment_content").val();
            {#默認爲空#}
            var pid = "";
            $.ajax({
                url: "/comment/",
                type: "post",
                data: {
                    content: content,
                    article_id: "{{ article_obj.pk }}",
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    console.log(data);
                    {#獲取3個值#}
                    var comment_time = data.timer;
                    var comment_content = data.content;
                    var comment_user = data.user;
                    {#組織li標籤#}
                    var $li = ` <li class="list-group-item">
                                       <div>
                                           <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                           <a href="">${comment_user}</a>
                                       </div>
                                       <div>
                                           <p>${comment_content}</p>
                                       </div>
                                    </li>`;
                    {#追加到評論列表中#}
                    $(".comment_list").append($li);
                    // 清空輸入框的內容
                    $("#comment_content").val("")
                }
            })

        })

        // 回覆按鈕事件
        $(".reply_btn").click(function () {
            {#獲取到textarea輸入框光標#}
            $("#comment_content").focus();
            {#增長@+用戶名+換行符#}
            var val = "@" + $(this).attr("username") + "\n";
            {#修改輸入框的值#}
            $("#comment_content").val(val);
            {#pid賦值#}
            pid=$(this).attr("comment_id");
            console.log(pid);

        })
    </script>

{% endblock %}
View Code

刷新網頁,發現自定義屬性comment_id以及被渲染出來了

點擊回覆,pid就能打印出來

正式提交子評論

修改article_detail.html,修改pid變量的位置

{% extends "base.html" %}

{% block content %}
    <div class="article_info">
        <h4 class="text-center">{{ article_obj.title }}</h4>
        <div class="content">
            {{ article_obj.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
        <div class="clearfix"></div>

        <div class="comment">
            <p>評論列表</p>
            <ul class="comment_list list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}樓</a>&nbsp;&nbsp;
                            <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                            <a href="">{{ comment.user.username }}</a>
                            <a href="javascript:void(0)" class="pull-right reply_btn"
                               username="{{ comment.user.username }}" comment_id="{{ comment.pk }}"><span>回覆</span></a>

                        </div>
                        <div>
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
                {% endfor %}

            </ul>
            <p>發表評論</p>
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <div>
                <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            </div>
            <input type="button" value="submit" class="btn btn-default comment_btn">
        </div>

    </div>
    {% csrf_token %}
    <script>
        // 點贊和踩滅
        $(".action").click(function () {
            {#hasClass() 方法檢查被選元素是否包含指定的 class#}
            var is_up = $(this).hasClass("diggit");
            {#獲取提示的span標籤#}
            var _this = $(this).children("span");
            {#判斷是否登陸#}
            if ("{{ request.user.username }}") {
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        is_up: is_up,
                        article_id: "{{ article_obj.pk }}",
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        console.log(typeof data);
                        if (data.state) {
                            //提交成功
                            var val = _this.text();  //獲取text值
                            //在原有的基礎上加1。注意:必定要進行類型轉換
                            _this.text(parseInt(val) + 1)
                        } else {
                            // 重複提交
                            var val = data.handled ? "您已經推薦過!" : "您已經反對過!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")  //清空提示文字
                            }, 1000)
                        }

                    }
                })
            } else {
                location.href = "/login/";
            }

        })

        // 提交評論
        var pid="";  //默認爲空,表示父評論
        $(".comment_btn").click(function () {
            {#評論內容#}
            var content = $("#comment_content").val();

            $.ajax({
                url: "/comment/",
                type: "post",
                data: {
                    content: content,
                    article_id: "{{ article_obj.pk }}",
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    console.log(data);
                    {#獲取3個值#}
                    var comment_time = data.timer;
                    var comment_content = data.content;
                    var comment_user = data.user;
                    {#組織li標籤#}
                    var $li = ` <li class="list-group-item">
                                       <div>
                                           <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                           <a href="">${comment_user}</a>
                                       </div>
                                       <div>
                                           <p>${comment_content}</p>
                                       </div>
                                    </li>`;
                    {#追加到評論列表中#}
                    $(".comment_list").append($li);
                    // 清空輸入框的內容
                    $("#comment_content").val("")
                }
            })

        })

        // 回覆按鈕事件
        $(".reply_btn").click(function () {
            {#獲取到textarea輸入框光標#}
            $("#comment_content").focus();
            {#增長@+用戶名+換行符#}
            var val = "@" + $(this).attr("username") + "\n";
            {#修改輸入框的值#}
            $("#comment_content").val(val);
            {#pid賦值#}
            pid=$(this).attr("comment_id");
            console.log(pid);

        })
    </script>

{% endblock %}
View Code

提交一條子評論

效果以下:

查看blog_comment表記錄,多了一條記錄

nid對應的是parent_comment_id

可是評論內容有些不妥,它加了@xiao

去掉@用戶名

怎麼去掉@用戶名呢?使用正則匹配?太麻煩了!

使用切片?好比s.slice(1),貌似很差作!

觀察一下規律,在輸入框中。第一行,永遠是@用戶名。第二行,纔是用戶真正的評論內容。

indexOf()

indexOf() 方法可返回某個指定的字符串值在字符串中首次出現的位置。

若是沒有找到匹配的字符串則返回 -1

 

思路:先找到換行符,使用indexOf。再加1,表示下一行,就能夠獲得真正的評論內容

先來測試一下,在templates目錄中,新建一個test.html,內容以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var aa = "@xiao\n34324234";
    var index = aa.indexOf("\n");
    content = aa.slice(index+1);
    console.log(content);

</script>
</body>
</html>
View Code

直接用谷歌瀏覽器打開,注意:這已經脫離了django,是直接訪問的。

能夠直接獲得評論內容,說明是可行的!

修改article_detail.html

{% extends "base.html" %}

{% block content %}
    <div class="article_info">
        <h4 class="text-center">{{ article_obj.title }}</h4>
        <div class="content">
            {{ article_obj.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
        <div class="clearfix"></div>

        <div class="comment">
            <p>評論列表</p>
            <ul class="comment_list list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}樓</a>&nbsp;&nbsp;
                            <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                            <a href="">{{ comment.user.username }}</a>
                            <a href="javascript:void(0)" class="pull-right reply_btn"
                               username="{{ comment.user.username }}" comment_id="{{ comment.pk }}"><span>回覆</span></a>

                        </div>
                        <div>
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
                {% endfor %}

            </ul>
            <p>發表評論</p>
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <div>
                <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            </div>
            <input type="button" value="submit" class="btn btn-default comment_btn">
        </div>

    </div>
    {% csrf_token %}
    <script>
        // 點贊和踩滅
        $(".action").click(function () {
            {#hasClass() 方法檢查被選元素是否包含指定的 class#}
            var is_up = $(this).hasClass("diggit");
            {#獲取提示的span標籤#}
            var _this = $(this).children("span");
            {#判斷是否登陸#}
            if ("{{ request.user.username }}") {
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        is_up: is_up,
                        article_id: "{{ article_obj.pk }}",
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        console.log(typeof data);
                        if (data.state) {
                            //提交成功
                            var val = _this.text();  //獲取text值
                            //在原有的基礎上加1。注意:必定要進行類型轉換
                            _this.text(parseInt(val) + 1)
                        } else {
                            // 重複提交
                            var val = data.handled ? "您已經推薦過!" : "您已經反對過!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")  //清空提示文字
                            }, 1000)
                        }

                    }
                })
            } else {
                location.href = "/login/";
            }

        })

        // 提交評論
        var pid = "";  //默認爲空,表示父評論
        $(".comment_btn").click(function () {
            {#評論內容#}
            var content = $("#comment_content").val();
            if (pid) {  //判斷pid不爲空時
                var index = content.indexOf("\n");
                //獲取評論內容,注意:此時content變量已經被替換掉了
                content = content.slice(index + 1)
            }
            $.ajax({
                url: "/comment/",
                type: "post",
                data: {
                    content: content,
                    article_id: "{{ article_obj.pk }}",
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    console.log(data);
                    {#獲取3個值#}
                    var comment_time = data.timer;
                    var comment_content = data.content;
                    var comment_user = data.user;
                    {#組織li標籤#}
                    var $li = ` <li class="list-group-item">
                                       <div>
                                           <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                           <a href="">${comment_user}</a>
                                       </div>
                                       <div>
                                           <p>${comment_content}</p>
                                       </div>
                                    </li>`;
                    {#追加到評論列表中#}
                    $(".comment_list").append($li);
                    // 清空輸入框的內容
                    $("#comment_content").val("")
                }
            })

        })

        // 回覆按鈕事件
        $(".reply_btn").click(function () {
            {#獲取到textarea輸入框光標#}
            $("#comment_content").focus();
            {#增長@+用戶名+換行符#}
            var val = "@" + $(this).attr("username") + "\n";
            {#修改輸入框的值#}
            $("#comment_content").val(val);
            {#pid賦值#}
            pid = $(this).attr("comment_id");
            console.log(pid);

        })
    </script>

{% endblock %}
View Code

刷新網頁,從新提交一次評論內容

刷新網頁,效果以下:

查看blog_comment表記錄,多了一條記錄

展現子評論效果

上面已經把評論入庫了,可是網頁,看不出來,哪一條是子評論

因此須要修改頁面,判斷parent_comment_id不爲空時,加一個div樣式

修改article_detail.html

{% extends "base.html" %}

{% block content %}
    <div class="article_info">
        <h4 class="text-center">{{ article_obj.title }}</h4>
        <div class="content">
            {{ article_obj.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips">
            </div>
        </div>
        <div class="clearfix"></div>

        <div class="comment">
            <p>評論列表</p>
            <ul class="comment_list list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}樓</a>&nbsp;&nbsp;
                            <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                            <a href="">{{ comment.user.username }}</a>
                            <a href="javascript:void(0)" class="pull-right reply_btn"
                               username="{{ comment.user.username }}" comment_id="{{ comment.pk }}"><span>回覆</span></a>

                        </div>
                        {#判斷parent_comment_id是否爲空#}
                        {% if comment.parent_comment_id %}
                            {#增長div樣式#}
                            <div class="parent_comment_info well">
                                <p>
                                    {#comment.parent_comment.user表示父評論的用戶名#}
                                    @{{ comment.parent_comment.user }}: {{ comment.parent_comment.content }}
                                </p>
                            </div>
                        {% endif %}
                        {#評論內容#}
                        <div>
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
                {% endfor %}

            </ul>
            <p>發表評論</p>
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <div>
                <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            </div>
            <input type="button" value="submit" class="btn btn-default comment_btn">
        </div>

    </div>
    {% csrf_token %}
    <script>
        // 點贊和踩滅
        $(".action").click(function () {
            {#hasClass() 方法檢查被選元素是否包含指定的 class#}
            var is_up = $(this).hasClass("diggit");
            {#獲取提示的span標籤#}
            var _this = $(this).children("span");
            {#判斷是否登陸#}
            if ("{{ request.user.username }}") {
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        is_up: is_up,
                        article_id: "{{ article_obj.pk }}",
                        csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                    },
                    success: function (data) {
                        console.log(data);
                        console.log(typeof data);
                        if (data.state) {
                            //提交成功
                            var val = _this.text();  //獲取text值
                            //在原有的基礎上加1。注意:必定要進行類型轉換
                            _this.text(parseInt(val) + 1)
                        } else {
                            // 重複提交
                            var val = data.handled ? "您已經推薦過!" : "您已經反對過!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")  //清空提示文字
                            }, 1000)
                        }

                    }
                })
            } else {
                location.href = "/login/";
            }

        })

        // 提交評論
        var pid = "";  //默認爲空,表示父評論
        $(".comment_btn").click(function () {
            {#評論內容#}
            var content = $("#comment_content").val();
            if (pid) {  //判斷pid不爲空時
                var index = content.indexOf("\n");
                //獲取評論內容,注意:此時content變量已經被替換掉了
                content = content.slice(index + 1)
            }
            $.ajax({
                url: "/comment/",
                type: "post",
                data: {
                    content: content,
                    article_id: "{{ article_obj.pk }}",
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                success: function (data) {
                    console.log(data);
                    {#獲取3個值#}
                    var comment_time = data.timer;
                    var comment_content = data.content;
                    var comment_user = data.user;
                    {#組織li標籤#}
                    var $li = ` <li class="list-group-item">
                                       <div>
                                           <span class="small">${comment_time}</span>&nbsp;&nbsp;
                                           <a href="">${comment_user}</a>
                                       </div>
                                       <div>
                                           <p>${comment_content}</p>
                                       </div>
                                    </li>`;
                    {#追加到評論列表中#}
                    $(".comment_list").append($li);
                    // 清空輸入框的內容
                    $("#comment_content").val("")
                }
            })

        })

        // 回覆按鈕事件
        $(".reply_btn").click(function () {
            {#獲取到textarea輸入框光標#}
            $("#comment_content").focus();
            {#增長@+用戶名+換行符#}
            var val = "@" + $(this).attr("username") + "\n";
            {#修改輸入框的值#}
            $("#comment_content").val(val);
            {#pid賦值#}
            pid = $(this).attr("comment_id");
            console.log(pid);

        })
    </script>

{% endblock %}
View Code

刪除有@xiao的表記錄

從新刷新頁面,效果以下:

 

2、後臺管理頁面

主要作如下2部分:

添加文章以及展現文章,下圖是博客園的效果:

修改index.html,增長一個下來選項,用來進入後臺管理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/js/jquery.js"></script>
    <script src="/static/bootstrap/js/bootstrap.js"></script>
    <style>
        .desc {
            text-align: justify;
        }

        .info {
            margin-top: 10px;
        }

        h5 a {
            color: #105cb6;
            font-size: 14px;
            font-weight: bold;
            text-decoration: underline;
        }

        .diggit {
            float: left;
            margin-right: 20px;
            width: 46px;
            height: 52px;
            background-image: url('/static/img/upup.gif');
        }

        .diggnum {
            position: relative;
            top: 6px;
            left: 20px;
        }

    </style>
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">博客園</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">新聞 <span class="sr-only">(current)</span></a></li>
                <li><a href="#">博問</a></li>

            </ul>

            <ul class="nav navbar-nav navbar-right">
                {% if request.user.username %}
                    <li><a href="#"><span class="glyphicon glyphicon-user"></span>&nbsp;{{ request.user.username }}</a>
                    </li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">管理<span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">修改密碼</a></li>
                            <li><a href="#">我的信息</a></li>
                            <li><a href="/backend/">後臺管理</a></li>
                            <li><a href="/logout/">註銷</a></li>
                            <li role="separator" class="divider"></li>
                        </ul>
                    </li>

                {% else %}
                    <li><a href="/login/">登錄</a></li>
                    <li><a href="#">註冊</a></li>
                {% endif %}


            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>

            <div class="panel panel-danger">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
        </div>
        <div class="col-md-6">

            <div class="article_list">
                {% for article in article_list %}
                    <div class="diggit">
                        <span class="diggnum">0</span>
                    </div>
                    <div class="article_item">

                        <h5><a href="/{{ article.user.username }}/articles/{{ article.pk }}">{{ article.title }}</a></h5>
                        <div>
                            <span class="media-left"><a href="/{{ article.user.username }}"><img width="48" height="48"
                                    src="https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1758343206,1224786249&fm=58&bpow=1024&bpoh=1536"
                                    alt=""></a></span>

                            <span class="media-right small desc ">
                              {{ article.desc }}
                          </span>

                        </div>
                        <div class="info small">
                            <span><a href="/{{ article.user.username }}">{{ article.user.username }}</a></span> &nbsp;
                            發佈於 <span>{{ article.create_time|date:'Y-m-d H:i' }}</span>&nbsp;&nbsp;
                            <img src="/static/img/icon_comment.gif" alt=""><a
                                href="/{{ article.user.username }}/articles/{{ article.pk }}">評論({{ article.comment_count }})</a>&nbsp;&nbsp;
                            <span class="glyphicon glyphicon-thumbs-up"></span><a href="/{{ article.user.username }}/articles/{{ article.pk }}">點贊({{ article.up_count }})</a>
                        </div>
                    </div>
                    <hr>
                {% endfor %}
            </div>


        </div>
        <div class="col-md-3">

            <div class="panel panel-warning">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
            <div class="panel panel-success">
                <div class="panel-heading">
                    <h3 class="panel-title">Panel title</h3>
                </div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>

        </div>
    </div>
</div>

</body>
</html>
View Code

注意:連接不須要增長用戶名,看博客園的後臺管理的連接,每一個人都是同樣的。

那麼它如何區分每個用戶呢?使用cookie和session來驗證!

修改urls.py,增長路徑backend

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
    path('logout/', views.logout),
    path('', views.index),
    #點贊或者踩滅
    path('digg/', views.digg),
    # 評論
    path('comment/', views.comment),
    # 後臺管理
    path('backend/', views.backend),

    #文章詳情
    re_path('(?P<username>\w+)/articles/(?P<article_id>\d+)/$', views.article_detail),
    # 跳轉
    re_path('(?P<username>\w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),
    # 我的站點
    re_path('(?P<username>\w+)/$', views.homesite),
]
View Code

修改views.py,增長視圖函數backend

def backend(request):
    user = request.user
    #當前用戶文章列表
    article_list = Article.objects.filter(user=user)
    # 由於是在templates的下一層,因此須要指定目錄backend
    return render(request, "backend/backend.html", {"user":user,"article_list":article_list})
View Code

將文件夾backend拷貝到templates目錄下

將backend.css拷貝到static-->css目錄下

切換一個用戶,進入後臺管理頁面

http://127.0.0.1:8000/backend/

效果以下:

3、富文本編輯器kindeditor

富文本編輯器,Rich Text Editor, 簡稱 RTE, 它提供相似於 Microsoft Word 的編輯功能,容易被不會編寫 HTML 的用戶並須要設置各類文本格式的用戶所喜好。

修改urls.py,增長路徑

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
    path('logout/', views.logout),
    path('', views.index),
    #點贊或者踩滅
    path('digg/', views.digg),
    # 評論
    path('comment/', views.comment),
    # 後臺管理
    path('backend/', views.backend),
    # 添加文章
    path('backend/add_article/', views.add_article),

    #文章詳情
    re_path('(?P<username>\w+)/articles/(?P<article_id>\d+)/$', views.article_detail),
    # 跳轉
    re_path('(?P<username>\w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),
    # 我的站點
    re_path('(?P<username>\w+)/$', views.homesite),
]
View Code

修改views.py,增長add_article視圖函數

def add_article(request):
    return render(request, "backend/add_article.html")
View Code

修改add_article.html

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
       <div class="add_article">
         <div class="alert-success text-center">添加文章</div>

         <div class="add_article_region">
              <div class="title form-group">
                 <label for="">標題</label>
                 <div>
                     <input type="text" name="title">
                 </div>
             </div>

             <div class="content form-group">

                 <div>
                     <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                 </div>
             </div>
             <div>
                 <ul>
                     {% for cate in cate_list %}
                     <li>{{ cate.title }}<input type="radio" name="cate" value="{{ cate.pk }}"></li>
                     {% endfor %}
                 </ul>
                 <hr>
                <ul>
                    {% for tag in tags %}
                    <li>{{ tag.title }} <input type="checkbox" name="tags" value="{{ tag.pk }}"></li>
                    {% endfor %}
                    
                </ul>
             </div>
             <input type="submit" class="btn btn-default">

         </div>

    </div>
    </form>

{% endblock %}
View Code

訪問增長文章頁面

http://127.0.0.1:8000/backend/add_article/

假設用戶要添加一個標題一,那麼必需要輸入以下內容:

這還只是一個標題,若是有不少內容呢?用戶會瘋掉的!

查看博客園的編輯器,輸入一個標題一

 

點擊HTML按鈕,效果以下:

它會自動生成h1標籤,這纔是數據庫真正存儲的內容。

將字體變成紅色

再次點擊HTML

它會自動生成HTML代碼

若是沒有富文本編輯器,那麼須要本身寫html代碼,太痛苦了!

那麼利用富文本編輯器提供的這些標籤,用戶不須要本身寫html代碼,它就能自動生成相應的html標籤以及樣式

博客園使用的是TinyMCE編輯器,接下來咱們使用的是kindeditor編輯器

kindeditor使用

進入kindeditor官網:

http://kindeditor.net/demo.php

點擊右側的下載:

http://kindeditor.net/down.php

下載最新版本

下載後,解壓壓縮包,將kindeditor文件夾放到static目錄

點擊文檔

http://kindeditor.net/doc.php

點擊使用方法

http://kindeditor.net/docs/usage.html

修改add_article.html,複製上面的js代碼,修改一下路徑

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
        <div class="add_article">
            <div class="alert-success text-center">添加文章</div>

            <div class="add_article_region">
                <div class="title form-group">
                    <label for="">標題</label>
                    <div>
                        <input type="text" name="title">
                    </div>
                </div>

                <div class="content form-group">
                    <label for="">內容(Kindeditor編輯器,不支持拖放/粘貼上傳圖片) </label>
                    <div>
                        <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                    </div>
                </div>
                <div>
                    <ul>
                        {% for cate in cate_list %}
                            <li>{{ cate.title }}<input type="radio" name="cate" value="{{ cate.pk }}"></li>
                        {% endfor %}
                    </ul>
                    <hr>
                    <ul>
                        {% for tag in tags %}
                            <li>{{ tag.title }} <input type="checkbox" name="tags" value="{{ tag.pk }}"></li>
                        {% endfor %}

                    </ul>
                </div>
                <input type="submit" class="btn btn-default">

            </div>

        </div>
    </form>
    {#引入kindeditor編輯器#}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            {#create裏面的值是textarea標籤的id值#}
            window.editor = K.create('#article_content');
        });
    </script>

{% endblock %}
View Code

刷新頁面,效果以下:

此時的編輯框,是能夠拖動的。若是不想讓它能拖動,怎麼辦呢?

查看編輯器初始化參數文檔

http://kindeditor.net/docs/option.html

固定寬高使用下面2個參數

拖動使用下面的參數

修改add_article.html

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
        <div class="add_article">
            <div class="alert-success text-center">添加文章</div>

            <div class="add_article_region">
                <div class="title form-group">
                    <label for="">標題</label>
                    <div>
                        <input type="text" name="title">
                    </div>
                </div>

                <div class="content form-group">
                    <label for="">內容(Kindeditor編輯器,不支持拖放/粘貼上傳圖片) </label>
                    <div>
                        <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                    </div>
                </div>
                <div>
                    <ul>
                        {% for cate in cate_list %}
                            <li>{{ cate.title }}<input type="radio" name="cate" value="{{ cate.pk }}"></li>
                        {% endfor %}
                    </ul>
                    <hr>
                    <ul>
                        {% for tag in tags %}
                            <li>{{ tag.title }} <input type="checkbox" name="tags" value="{{ tag.pk }}"></li>
                        {% endfor %}

                    </ul>
                </div>
                <input type="submit" class="btn btn-default">

            </div>

        </div>
    </form>
    {#引入kindeditor編輯器#}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            {#create裏面的值是textarea標籤的id值#}
            window.editor = K.create('#article_content', {
                {#固定寬高#}
                width: '100%',
                height: '600px',
                {#禁止拖動#}
                resizeType: 0,
            });
        });
    </script>

{% endblock %}
View Code

刷新頁面,效果以下:

這個參數,用來調整工具欄展現的。默認是展現全部,若是須要展現指定的,能夠修改這個值。

好比博客園的評論框,只有6個按鈕

功能對應表,在文檔中都有,好比:

上傳圖片

默認點擊上傳圖片後,它會一直轉圈,提示正在上傳。注意:等待是無心義的,它須要服務器返回一個json纔會中止。

須要設置上傳服務器的url才行。

默認使用的php,它提供了一些示例代碼,好比php目錄下的upload_json.php

還有其餘語言的demo,好比asp、asp.net、jsp

由於沒有提供python的,因此須要本身寫了!

修改add_article.html,增長參數uploadJson,指定上傳的請求url爲upload

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
        <div class="add_article">
            <div class="alert-success text-center">添加文章</div>

            <div class="add_article_region">
                <div class="title form-group">
                    <label for="">標題</label>
                    <div>
                        <input type="text" name="title">
                    </div>
                </div>

                <div class="content form-group">
                    <label for="">內容(Kindeditor編輯器,不支持拖放/粘貼上傳圖片) </label>
                    <div>
                        <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                    </div>
                </div>
                <div>
                    <ul>
                        {% for cate in cate_list %}
                            <li>{{ cate.title }}<input type="radio" name="cate" value="{{ cate.pk }}"></li>
                        {% endfor %}
                    </ul>
                    <hr>
                    <ul>
                        {% for tag in tags %}
                            <li>{{ tag.title }} <input type="checkbox" name="tags" value="{{ tag.pk }}"></li>
                        {% endfor %}

                    </ul>
                </div>
                <input type="submit" class="btn btn-default">

            </div>

        </div>
    </form>
    {#引入kindeditor編輯器#}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            {#create裏面的值是textarea標籤的id值#}
            window.editor = K.create('#article_content', {
                {#固定寬高#}
                width: '100%',
                height: '600px',
                {#禁止拖動#}
                resizeType: 0,
                {#上傳文件請求地址#}
                uploadJson:"/upload/",
            });
        });
    </script>

{% endblock %}
View Code

修改urls.py,增長路徑upload

"""cnblog URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path

from blog import  views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
    path('logout/', views.logout),
    path('', views.index),
    #點贊或者踩滅
    path('digg/', views.digg),
    # 評論
    path('comment/', views.comment),
    # 後臺管理
    path('backend/', views.backend),
    # 添加文章
    path('backend/add_article/', views.add_article),
    # 上傳文件
    path('upload/', views.upload),

    #文章詳情
    re_path('(?P<username>\w+)/articles/(?P<article_id>\d+)/$', views.article_detail),
    # 跳轉
    re_path('(?P<username>\w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),
    # 我的站點
    re_path('(?P<username>\w+)/$', views.homesite),
]
View Code

修改views.py,增長視圖函數upload

def upload(request):
    print(request.FILES)  # 打印文件對象
    return HttpResponse("ok")
View Code

刷新頁面,上傳一個圖片

提示上傳錯誤

爲何會返回403呢?由於被django的csrf組件阻止了。

因爲請求是kindeditor發的,它沒有帶csrf_token,因此被django給攔截了!

爲了預防這種狀況發生,kindeditor提供了extraFileUploadParams參數

修改add_article.html,在頁面中的任意位置增長{% csrf_token %}

修改js代碼

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
        <div class="add_article">
            <div class="alert-success text-center">添加文章</div>

            <div class="add_article_region">
                <div class="title form-group">
                    <label for="">標題</label>
                    <div>
                        <input type="text" name="title">
                    </div>
                </div>

                <div class="content form-group">
                    <label for="">內容(Kindeditor編輯器,不支持拖放/粘貼上傳圖片) </label>
                    <div>
                        <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                    </div>
                </div>
                <div>
                    <ul>
                        {% for cate in cate_list %}
                            <li>{{ cate.title }}<input type="radio" name="cate" value="{{ cate.pk }}"></li>
                        {% endfor %}
                    </ul>
                    <hr>
                    <ul>
                        {% for tag in tags %}
                            <li>{{ tag.title }} <input type="checkbox" name="tags" value="{{ tag.pk }}"></li>
                        {% endfor %}

                    </ul>
                </div>
                <input type="submit" class="btn btn-default">

            </div>

        </div>
    </form>
    {% csrf_token %}
    {#引入kindeditor編輯器#}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            {#create裏面的值是textarea標籤的id值#}
            window.editor = K.create('#article_content', {
                {#固定寬高#}
                width: '100%',
                height: '600px',
                {#禁止拖動#}
                resizeType: 0,
                {#上傳文件請求地址#}
                uploadJson: "/upload/",
                {#添加別的參數csrfmiddlewaretoken#}
                extraFileUploadParams: {
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
            });
        });
    </script>

{% endblock %}
View Code

從新上線,提示

查看Pycharm控制檯輸出:

<MultiValueDict: {'imgFile': [<InMemoryUploadedFile: 161022vkhyigaq4si947qv.jpg (image/jpeg)>]}>

它的key爲imgFile,這個是kindeditor默認定義的form名稱。

若是須要改動的話,增長一個參數filePostName

 

修改add_article.html

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
        <div class="add_article">
            <div class="alert-success text-center">添加文章</div>

            <div class="add_article_region">
                <div class="title form-group">
                    <label for="">標題</label>
                    <div>
                        <input type="text" name="title">
                    </div>
                </div>

                <div class="content form-group">
                    <label for="">內容(Kindeditor編輯器,不支持拖放/粘貼上傳圖片) </label>
                    <div>
                        <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                    </div>
                </div>
                <div>
                    <ul>
                        {% for cate in cate_list %}
                            <li>{{ cate.title }}<input type="radio" name="cate" value="{{ cate.pk }}"></li>
                        {% endfor %}
                    </ul>
                    <hr>
                    <ul>
                        {% for tag in tags %}
                            <li>{{ tag.title }} <input type="checkbox" name="tags" value="{{ tag.pk }}"></li>
                        {% endfor %}

                    </ul>
                </div>
                <input type="submit" class="btn btn-default">

            </div>

        </div>
    </form>
    {% csrf_token %}
    {#引入kindeditor編輯器#}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            {#create裏面的值是textarea標籤的id值#}
            window.editor = K.create('#article_content', {
                {#固定寬高#}
                width: '100%',
                height: '600px',
                {#禁止拖動#}
                resizeType: 0,
                {#上傳文件請求地址#}
                uploadJson: "/upload/",
                {#添加別的參數csrfmiddlewaretoken#}
                extraFileUploadParams: {
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                {#指定上傳文件form名稱#}
                filePostName:"upload_img"
            });
        });
    </script>

{% endblock %}
View Code

修改upload視圖函數,存儲圖片。注意導入os模塊以及settings,完整代碼以下:

from django.shortcuts import render, HttpResponse, redirect
from django.contrib import auth
from blog.models import Article, UserInfo, Blog, Category, Tag, ArticleUpDown, Comment
from django.db.models import Sum, Avg, Max, Min, Count
from django.db.models import F
import json
from django.http import JsonResponse
from django.db import transaction
import os
from cnblog import settings  # 導入settings。注意:cnblog爲項目名


# Create your views here.
def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        # 用戶驗證成功,返回user對象,不然返回None
        user = auth.authenticate(username=user, password=pwd)
        if user:
            # 登陸,註冊session
            # 全局變量 request.user=當前登錄對象(session中)
            auth.login(request, user)
            return redirect("/index/")

    return render(request, "login.html")


def index(request):
    article_list = Article.objects.all()
    return render(request, "index.html", {"article_list": article_list})


def logout(request):  # 註銷
    auth.logout(request)
    return redirect("/index/")


def query_current_site(request, username):  # 查詢當前站點的博客標題
    # 查詢當前站點的用戶對象
    user = UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, "not_found.html")
    # 查詢當前站點對象
    blog = user.blog
    return blog


def homesite(request, username, **kwargs):  # 我的站點主頁
    print("kwargs", kwargs)

    blog = query_current_site(request, username)

    # 查詢當前用戶發佈的全部文章
    if not kwargs:
        article_list = Article.objects.filter(user__username=username)
    else:
        condition = kwargs.get("condition")
        params = kwargs.get("params")
        # 判斷分類、隨筆、歸檔
        if condition == "category":
            article_list = Article.objects.filter(user__username=username).filter(category__title=params)
        elif condition == "tag":
            article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
        else:
            year, month = params.split("/")
            article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                  create_time__month=month)
    return render(request, "homesite.html", {"blog": blog, "username": username, "article_list": article_list})


def article_detail(request,username,article_id):
    blog = query_current_site(request,username)

    #查詢指定id的文章
    article_obj = Article.objects.filter(pk=article_id).first()
    user_id = UserInfo.objects.filter(username=username).first().nid

    comment_list = Comment.objects.filter(article_id=article_id)
    dict = {"blog":blog,
            "username":username,
            'article_obj':article_obj,
            "user_id":user_id,
            "comment_list":comment_list,
            }

    return render(request,'article_detail.html',dict)

def digg(request):
    print(request.POST)
    if request.method == "POST":
        # ajax發送的過來的true和false是字符串,使用json反序列化獲得布爾值
        is_up = json.loads(request.POST.get("is_up"))
        article_id = request.POST.get("article_id")
        user_id = request.user.pk

        response = {"state": True, "msg": None}  # 初始狀態
        # 判斷當前登陸用戶是否對這篇文章作過點贊或者踩滅操做
        obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
        if obj:
            response["state"] = False  # 更改狀態
            response["handled"] = obj.is_up  # 獲取以前的操做,返回true或者false
            print(obj.is_up)
        else:
            with transaction.atomic():
                # 插入一條記錄
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                if is_up:  # 判斷爲推薦
                    Article.objects.filter(pk=article_id).update(up_count=F("up_count") + 1)
                else:  # 反對
                    Article.objects.filter(pk=article_id).update(down_count=F("down_count") + 1)

        return JsonResponse(response)

    else:
        return HttpResponse("非法請求")


def comment(request):
    print(request.POST)
    if request.method == "POST":
        # 獲取數據
        user_id = request.user.pk
        article_id = request.POST.get("article_id")
        content = request.POST.get("content")
        pid = request.POST.get("pid")
        # 生成評論對象
        with transaction.atomic():  # 增長事務
            # 評論表增長一條記錄
            comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                             parent_comment_id=pid)
            # 當前文章的評論數加1
            Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)

        response = {"state": False}  # 初始狀態

        if comment.user_id:  # 判斷返回值
            response = {"state": True}

        # 響應體增長3個變量
        response["timer"] = comment.create_time.strftime("%Y-%m-%d %X")
        response["content"] = comment.content
        response["user"] = request.user.username

        return JsonResponse(response)  # 返回json對象

    else:
        return HttpResponse("非法請求")

def backend(request):
    user = request.user
    #當前用戶文章列表
    article_list = Article.objects.filter(user=user)
    # 由於是在templates的下一層,因此須要指定目錄backend
    return render(request, "backend/backend.html", {"user":user,"article_list":article_list})

def add_article(request):
    return render(request, "backend/add_article.html")

def upload(request):
    print(request.FILES)
    obj = request.FILES.get("upload_img")  # 獲取文件對象
    name = obj.name  # 文件名
    #文件存儲的絕對路徑
    path = os.path.join(settings.BASE_DIR, "static", "upload", name)
    with open(path, "wb") as f:
        for line in obj:  # 遍歷文件對象
            f.write(line)  # 寫入文件

    #必須返回這2個key
    res = {
        # 爲0表示沒有錯誤,若是有錯誤,設置爲1。增長一個key爲message,用來顯示指定的錯誤
        "error": 0,
        # 圖片訪問路徑,必須可以直接訪問到
        "url": "/static/upload/" + name
    }

    return HttpResponse(json.dumps(res))  # 必須返回Json
View Code

在static目錄下,建立目錄upload

再次上傳

效果以下:

 

點擊圖片,右鍵屬性,能夠調整像素

進入upload目錄,能夠看到一張圖片

查看Html代碼

這個圖片路徑,是能夠直接訪問的!

 

那麼博客園也是這麼作的,上傳一個圖片以後,查看html代碼, 就能夠直接訪問圖片。

 

圖片連接,能夠直接訪問

 

增長文章類別和分類

修改add_article.html

{% extends 'backend/base.html' %}

{% block content %}

    <form action="" method="post">
        {% csrf_token %}
        <div class="add_article">
            <div class="alert-success text-center">添加文章</div>

            <div class="add_article_region">
                <div class="title form-group">
                    <label for="">標題</label>
                    <div>
                        <input type="text" name="title">
                    </div>
                </div>

                <div class="content form-group">
                    <label for="">內容(Kindeditor編輯器,不支持拖放/粘貼上傳圖片) </label>
                    <div>
                        <textarea name="content" id="article_content" cols="30" rows="10"></textarea>
                    </div>
                </div>
                <h4>分類</h4>
                <div class="form-group">
                <div class="radio">
                    {% for cate in cate_list %}
                        <label><input type="radio" name="cate" value="{{ cate.pk }}">{{ cate.title }}</label>
                    {% endfor %}
                </div>
                </div>
                <hr>
                <h4>標籤</h4>
                <div class="checkbox">
                    {% for tag in tags %}
                  <label>
                    <input type="checkbox" name="tags" value="{{ tag.pk }}">{{ tag.title }}
                  </label>
                    {% endfor %}
                </div>
                <hr>

                <input type="submit" class="btn btn-default">

            </div>

        </div>
    </form>
    {% csrf_token %}
    {#引入kindeditor編輯器#}
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            {#create裏面的值是textarea標籤的id值#}
            window.editor = K.create('#article_content', {
                {#固定寬高#}
                width: '100%',
                height: '600px',
                {#禁止拖動#}
                resizeType: 0,
                {#上傳文件請求地址#}
                uploadJson: "/upload/",
                {#添加別的參數csrfmiddlewaretoken#}
                extraFileUploadParams: {
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
                },
                {#指定上傳文件form名稱#}
                filePostName:"upload_img"
            });
        });
    </script>

{% endblock %}
View Code

修改add_article視圖函數

from django.shortcuts import render,HttpResponse,redirect
from django.contrib import auth
from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown,Comment,Article2Tag
from django.db.models import Sum,Avg,Max,Min,Count
from django.db.models import F
import json
from django.http import JsonResponse
from django.db import transaction
from cnblog import settings  # 導入settings。注意:cnblog爲項目名
import os

# Create your views here.
def login(request):

    if request.method=="POST":
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")
        # 用戶驗證成功,返回user對象,不然返回None
        user=auth.authenticate(username=user,password=pwd)
        if user:
            # 登陸,註冊session
            # 全局變量 request.user=當前登錄對象(session中)
            auth.login(request,user)
            return redirect("/index/")

    return render(request,"login.html")

def index(request):
    article_list=Article.objects.all()
    return render(request,"index.html",{"article_list":article_list})

def logout(request):  # 註銷
    auth.logout(request)
    return redirect("/index/")

def query_current_site(request,username):  # 查詢當前站點的博客標題
    # 查詢當前站點的用戶對象
    user = UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, "not_found.html")
    # 查詢當前站點對象
    blog = user.blog
    return blog

def homesite(request,username,**kwargs):  # 我的站點主頁
    print("kwargs", kwargs)

    blog = query_current_site(request,username)

    # 查詢當前用戶發佈的全部文章
    if not kwargs:
        article_list = Article.objects.filter(user__username=username)
    else:
        condition = kwargs.get("condition")
        params = kwargs.get("params")
        #判斷分類、隨筆、歸檔
        if condition == "category":
            article_list = Article.objects.filter(user__username=username).filter(category__title=params)
        elif condition == "tag":
            article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
        else:
            year, month = params.split("/")
            article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                  create_time__month=month)
    return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})

def article_detail(request,username,article_id):
    blog = query_current_site(request,username)

    #查詢指定id的文章
    article_obj = Article.objects.filter(pk=article_id).first()
    user_id = UserInfo.objects.filter(username=username).first().nid

    comment_list = Comment.objects.filter(article_id=article_id)
    dict = {"blog":blog,
            "username":username,
            'article_obj':article_obj,
            "user_id":user_id,
            "comment_list":comment_list,
            }

    return render(request,'article_detail.html',dict)

def digg(request):
    print(request.POST)
    if request.method == "POST":
        #ajax發送的過來的true和false是字符串,使用json反序列化獲得布爾值
        is_up = json.loads(request.POST.get("is_up"))
        article_id = request.POST.get("article_id")
        user_id = request.user.pk

        response = {"state": True, "msg": None}  # 初始狀態
        #判斷當前登陸用戶是否對這篇文章作過點贊或者踩滅操做
        obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
        if obj:
            response["state"] = False  # 更改狀態
            response["handled"] = obj.is_up  # 獲取以前的操做,返回true或者false
            print(obj.is_up)
        else:
            with transaction.atomic():
                #插入一條記錄
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                if is_up: # 判斷爲推薦
                    Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                else: # 反對
                    Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)

        return JsonResponse(response)

    else:
        return HttpResponse("非法請求")

def comment(request):
    print(request.POST)
    if request.method == "POST":
        # 獲取數據
        user_id = request.user.pk
        article_id = request.POST.get("article_id")
        content = request.POST.get("content")
        pid = request.POST.get("pid")
        # 生成評論對象
        with transaction.atomic():  # 增長事務
            # 評論表增長一條記錄
            comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
            # 當前文章的評論數加1
            Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)

        response = {"state": False}  # 初始狀態

        if comment.user_id:  # 判斷返回值
            response = {"state": True}

        #響應體增長3個變量
        response["timer"] = comment.create_time.strftime("%Y-%m-%d %X")
        response["content"] = comment.content
        response["user"] = request.user.username

        return JsonResponse(response)  # 返回json對象

    else:
        return HttpResponse("非法請求")

def backend(request):  # 後臺管理
    user = request.user
    #當前用戶文章列表
    article_list = Article.objects.filter(user=user)
    # 由於是在templates的下一層,因此須要指定目錄backend
    return render(request, "backend/backend.html", {"user":user,"article_list":article_list})

def add_article(request):
    if request.method=="POST":
        title=request.POST.get("title")
        content=request.POST.get("content")
        user=request.user
        cate_pk=request.POST.get("cate")
        tags_pk_list=request.POST.getlist("tags")

        # 切片文章文本
        desc=content[0:150] #文章描述
        #插入到Article表
        article_obj=Article.objects.create(title=title,content=content,user=user,category_id=cate_pk,desc=desc)

        for tag_pk in tags_pk_list:  #插入關係表
            #因爲是中間模型,只能安裝普通表查詢才行
            Article2Tag.objects.create(article_id=article_obj.pk,tag_id=tag_pk)

        return redirect("/backend/")  # 跳轉後臺首頁
    
    else:
        blog = request.user.blog
        cate_list = Category.objects.filter(blog=blog)
        tags = Tag.objects.filter(blog=blog)
        dict = {
            "blog":blog,
            "cate_list":cate_list,
            "tags":tags,
        }

    return render(request, "backend/add_article.html",dict)

def upload(request):
    print(request.FILES)
    obj = request.FILES.get("upload_img")  # 獲取文件對象
    name = obj.name  # 文件名
    #文件存儲的絕對路徑
    path = os.path.join(settings.BASE_DIR, "static", "upload", name)
    with open(path, "wb") as f:
        for line in obj:  # 遍歷文件對象
            f.write(line)  # 寫入文件

    #必須返回這2個key
    res = {
        # 爲0表示沒有錯誤,若是有錯誤,設置爲1。增長一個key爲message,用來顯示指定的錯誤
        "error": 0,
        # 圖片訪問路徑,必須可以直接訪問到
        "url": "/static/upload/" + name
    }

    return HttpResponse(json.dumps(res))  # 必須返回Json
View Code

注意:中間模型表Article2Tag不能直接用add,必須當作普通表處理才行!

 

刷新頁面,添加一篇文章

 

提交以後,跳轉後臺管理頁面

desc bug

首頁查看

http://127.0.0.1:8000/

發現文章描述都是html標籤

這是爲何呢?由於desc是從文本框內容截取前150個字符串,因爲內容都是html標籤。因此就形成了上面的現象!

注意:index.html中的 {{article.desc}} 不能加sale,不然頁面會錯亂!

 

那麼如何去掉html標籤呢?使用bs4模塊

4、bs4模塊

bs4全名BeautifulSoup,是編寫python爬蟲經常使用庫之一,主要用來解析html標籤。

安裝bs4,使用下面的命令安裝

pip3 install bs4

新建一個test.py文件,內容以下:

from bs4 import BeautifulSoup
s="<div><div>Hello</div><p>Yuan</p></div><a>click</a><script>alert(123)</script>"

soup=BeautifulSoup(s,"html.parser")
print(soup.text)
View Code

兩個參數:第一個參數是要解析的html文本,第二個參數是使用那種解析器,對於HTML來說就是html.parser,這個是bs4自帶的解析器。

執行輸出:

HelloYuanclickalert(123)

 

經過bs4,就能夠解析html了,而後截圖150個字符串,就沒有問題了!

修改add_article視圖函數,注意導入BeautifulSoup模塊

 

from django.shortcuts import render, HttpResponse, redirect
from django.contrib import auth
from blog.models import Article, UserInfo, Blog, Category, Tag, ArticleUpDown, Comment, Article2Tag
from django.db.models import Sum, Avg, Max, Min, Count
from django.db.models import F
import json
from django.http import JsonResponse
from django.db import transaction
from cnblog import settings  # 導入settings。注意:cnblog爲項目名
import os
from bs4 import BeautifulSoup

# Create your views here.
def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        # 用戶驗證成功,返回user對象,不然返回None
        user = auth.authenticate(username=user, password=pwd)
        if user:
            # 登陸,註冊session
            # 全局變量 request.user=當前登錄對象(session中)
            auth.login(request, user)
            return redirect("/index/")

    return render(request, "login.html")


def index(request):
    article_list = Article.objects.all()
    return render(request, "index.html", {"article_list": article_list})


def logout(request):  # 註銷
    auth.logout(request)
    return redirect("/index/")


def query_current_site(request, username):  # 查詢當前站點的博客標題
    # 查詢當前站點的用戶對象
    user = UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, "not_found.html")
    # 查詢當前站點對象
    blog = user.blog
    return blog


def homesite(request, username, **kwargs):  # 我的站點主頁
    print("kwargs", kwargs)

    blog = query_current_site(request, username)

    # 查詢當前用戶發佈的全部文章
    if not kwargs:
        article_list = Article.objects.filter(user__username=username)
    else:
        condition = kwargs.get("condition")
        params = kwargs.get("params")
        # 判斷分類、隨筆、歸檔
        if condition == "category":
            article_list = Article.objects.filter(user__username=username).filter(category__title=params)
        elif condition == "tag":
            article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
        else:
            year, month = params.split("/")
            article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                  create_time__month=month)
    return render(request, "homesite.html", {"blog": blog, "username": username, "article_list": article_list})


def article_detail(request, username, article_id):
    blog = query_current_site(request, username)

    # 查詢指定id的文章
    article_obj = Article.objects.filter(pk=article_id).first()
    user_id = UserInfo.objects.filter(username=username).first().nid

    comment_list = Comment.objects.filter(article_id=article_id)
    dict = {"blog": blog,
            "username": username,
            'article_obj': article_obj,
            "user_id": user_id,
            "comment_list": comment_list,
            }

    return render(request, 'article_detail.html', dict)


def digg(request):
    print(request.POST)
    if request.method == "POST":
        # ajax發送的過來的true和false是字符串,使用json反序列化獲得布爾值
        is_up = json.loads(request.POST.get("is_up"))
        article_id = request.POST.get("article_id")
        user_id = request.user.pk

        response = {"state": True, "msg": None}  # 初始狀態
        # 判斷當前登陸用戶是否對這篇文章作過點贊或者踩滅操做
        obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
        if obj:
            response["state"] = False  # 更改狀態
            response["handled"] = obj.is_up  # 獲取以前的操做,返回true或者false
            print(obj.is_up)
        else:
            with transaction.atomic():
                # 插入一條記錄
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                if is_up:  # 判斷爲推薦
                    Article.objects.filter(pk=article_id).update(up_count=F("up_count") + 1)
                else:  # 反對
                    Article.objects.filter(pk=article_id).update(down_count=F("down_count") + 1)

        return JsonResponse(response)

    else:
        return HttpResponse("非法請求")


def comment(request):
    print(request.POST)
    if request.method == "POST":
        # 獲取數據
        user_id = request.user.pk
        article_id = request.POST.get("article_id")
        content = request.POST.get("content")
        pid = request.POST.get("pid")
        # 生成評論對象
        with transaction.atomic():  # 增長事務
            # 評論表增長一條記錄
            comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                             parent_comment_id=pid)
            # 當前文章的評論數加1
            Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)

        response = {"state": False}  # 初始狀態

        if comment.user_id:  # 判斷返回值
            response = {"state": True}

        # 響應體增長3個變量
        response["timer"] = comment.create_time.strftime("%Y-%m-%d %X")
        response["content"] = comment.content
        response["user"] = request.user.username

        return JsonResponse(response)  # 返回json對象

    else:
        return HttpResponse("非法請求")


def backend(request):  # 後臺管理
    user = request.user
    # 當前用戶文章列表
    article_list = Article.objects.filter(user=user)
    # 由於是在templates的下一層,因此須要指定目錄backend
    return render(request, "backend/backend.html", {"user": user, "article_list": article_list})


def add_article(request):
    if request.method=="POST":
        title=request.POST.get("title")
        content=request.POST.get("content")
        user=request.user
        cate_pk=request.POST.get("cate")
        tags_pk_list=request.POST.getlist("tags")
        #使用BeautifulSoup過濾html標籤
        soup = BeautifulSoup(content, "html.parser")

        # 切片文章文本
        desc=soup.text[0:150] #文章描述
        #插入到Article表
        article_obj=Article.objects.create(title=title,content=content,user=user,category_id=cate_pk,desc=desc)

        for tag_pk in tags_pk_list:  #插入關係表
            #因爲是中間模型,只能安裝普通表查詢才行
            Article2Tag.objects.create(article_id=article_obj.pk,tag_id=tag_pk)

        return redirect("/backend/")  # 跳轉後臺首頁

    else:
        blog = request.user.blog
        cate_list = Category.objects.filter(blog=blog)
        tags = Tag.objects.filter(blog=blog)
        dict = {
            "blog":blog,
            "cate_list":cate_list,
            "tags":tags,
        }

    return render(request, "backend/add_article.html",dict)

def upload(request):
    print(request.FILES)
    obj = request.FILES.get("upload_img")  # 獲取文件對象
    name = obj.name  # 文件名
    # 文件存儲的絕對路徑
    path = os.path.join(settings.BASE_DIR, "static", "upload", name)
    with open(path, "wb") as f:
        for line in obj:  # 遍歷文件對象
            f.write(line)  # 寫入文件

    # 必須返回這2個key
    res = {
        # 爲0表示沒有錯誤,若是有錯誤,設置爲1。增長一個key爲message,用來顯示指定的錯誤
        "error": 0,
        # 圖片訪問路徑,必須可以直接訪問到
        "url": "/static/upload/" + name
    }

    return HttpResponse(json.dumps(res))  # 必須返回Json
View Code

 

刪除剛纔那篇文章,注意:使用admin後臺刪除,直接刪除表記錄,有外鍵關聯!

刷新頁面,從新添加一篇文章

刷新首頁,查看最後一篇文章

數據安全

上面添加一篇文件,看着貌似沒啥問題。那是由於沒有包含js代碼,若是有xss攻擊呢?

添加一篇文章

提交以後,刷新首頁

點擊文章標題

發現直接彈框了,這是不安全的!

 

怎麼解決這個問題呢?

應該入庫的時候,就把script標籤直接刪掉!

BeautifulSoup還有一個功能,能夠獲得html的標籤名,好比下面一段python代碼:

decompose()方法將當前節點移除文檔樹並徹底銷燬

from bs4 import BeautifulSoup
s="<div><div>Hello</div><p>Yuan</p></div><a>click</a><script>alert(123)</script>"

soup=BeautifulSoup(s,"html.parser")  # 解析Html

for tag in soup.find_all():
    print(tag.name)  # 打印標籤名

    if tag.name=="script":  # 判斷標籤名爲script
        tag.decompose()  # 移出標籤

print(str(soup))
View Code

執行輸出:

div
div
p
a
script
<div><div>Hello</div><p>Yuan</p></div><a>click</a>

能夠看到最後的script標籤被移除掉了!

 

修改add_article視圖函數,完整代碼以下:

from django.shortcuts import render,HttpResponse,redirect
from django.contrib import auth
from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown,Comment,Article2Tag
from django.db.models import Sum,Avg,Max,Min,Count
from django.db.models import F
import json
from django.http import JsonResponse
from django.db import transaction
from cnblog import settings  # 導入settings。注意:cnblog爲項目名
import os
from bs4 import BeautifulSoup

# Create your views here.
def login(request):

    if request.method=="POST":
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")
        # 用戶驗證成功,返回user對象,不然返回None
        user=auth.authenticate(username=user,password=pwd)
        if user:
            # 登陸,註冊session
            # 全局變量 request.user=當前登錄對象(session中)
            auth.login(request,user)
            return redirect("/index/")

    return render(request,"login.html")

def index(request):
    article_list=Article.objects.all()
    return render(request,"index.html",{"article_list":article_list})

def logout(request):  # 註銷
    auth.logout(request)
    return redirect("/index/")

def query_current_site(request,username):  # 查詢當前站點的博客標題
    # 查詢當前站點的用戶對象
    user = UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, "not_found.html")
    # 查詢當前站點對象
    blog = user.blog
    return blog

def homesite(request,username,**kwargs):  # 我的站點主頁
    print("kwargs", kwargs)

    blog = query_current_site(request,username)

    # 查詢當前用戶發佈的全部文章
    if not kwargs:
        article_list = Article.objects.filter(user__username=username)
    else:
        condition = kwargs.get("condition")
        params = kwargs.get("params")
        #判斷分類、隨筆、歸檔
        if condition == "category":
            article_list = Article.objects.filter(user__username=username).filter(category__title=params)
        elif condition == "tag":
            article_list = Article.objects.filter(user__username=username).filter(tags__title=params)
        else:
            year, month = params.split("/")
            article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,
                                                                                  create_time__month=month)
    return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})

def article_detail(request,username,article_id):
    blog = query_current_site(request,username)

    #查詢指定id的文章
    article_obj = Article.objects.filter(pk=article_id).first()
    user_id = UserInfo.objects.filter(username=username).first().nid

    comment_list = Comment.objects.filter(article_id=article_id)
    dict = {"blog":blog,
            "username":username,
            'article_obj':article_obj,
            "user_id":user_id,
            "comment_list":comment_list,
            }

    return render(request,'article_detail.html',dict)

def digg(request):
    print(request.POST)
    if request.method == "POST":
        #ajax發送的過來的true和false是字符串,使用json反序列化獲得布爾值
        is_up = json.loads(request.POST.get("is_up"))
        article_id = request.POST.get("article_id")
        user_id = request.user.pk

        response = {"state": True, "msg": None}  # 初始狀態
        #判斷當前登陸用戶是否對這篇文章作過點贊或者踩滅操做
        obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
        if obj:
            response["state"] = False  # 更改狀態
            response["handled"] = obj.is_up  # 獲取以前的操做,返回true或者false
            print(obj.is_up)
        else:
            with transaction.atomic():
                #插入一條記錄
                new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
                if is_up: # 判斷爲推薦
                    Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)
                else: # 反對
                    Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)

        return JsonResponse(response)

    else:
        return HttpResponse("非法請求")

def comment(request):
    print(request.POST)
    if request.method == "POST":
        # 獲取數據
        user_id = request.user.pk
        article_id = request.POST.get("article_id")
        content = request.POST.get("content")
        pid = request.POST.get("pid")
        # 生成評論對象
        with transaction.atomic():  # 增長事務
            # 評論表增長一條記錄
            comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
            # 當前文章的評論數加1
            Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)

        response = {"state": False}  # 初始狀態

        if comment.user_id:  # 判斷返回值
            response = {"state": True}

        #響應體增長3個變量
        response["timer"] = comment.create_time.strftime("%Y-%m-%d %X")
        response["content"] = comment.content
        response["user"] = request.user.username

        return JsonResponse(response)  # 返回json對象

    else:
        return HttpResponse("非法請求")

def backend(request):  # 後臺管理
    user = request.user
    #當前用戶文章列表
    article_list = Article.objects.filter(user=user)
    # 由於是在templates的下一層,因此須要指定目錄backend
    return render(request, "backend/backend.html", {"user":user,"article_list":article_list})

def add_article(request):
    if request.method=="POST":
        title=request.POST.get("title")
        content=request.POST.get("content")
        user=request.user
        cate_pk=request.POST.get("cate")
        tags_pk_list=request.POST.getlist("tags")
        #使用BeautifulSoup過濾html標籤
        soup = BeautifulSoup(content, "html.parser")
        # 文章過濾:
        for tag in soup.find_all():
            # print(tag.name)
            if tag.name in ["script", ]:  # 包含script標籤時
                tag.decompose()

        # 切片文章文本
        desc=soup.text[0:150] #文章描述
        #插入到Article表,注意content=str(soup)
        article_obj=Article.objects.create(title=title,content=str(soup),user=user,category_id=cate_pk,desc=desc)

        for tag_pk in tags_pk_list:  #插入關係表
            #因爲是中間模型,只能安裝普通表查詢才行
            Article2Tag.objects.create(article_id=article_obj.pk,tag_id=tag_pk)

        return redirect("/backend/")  # 跳轉後臺首頁

    else:
        blog = request.user.blog
        cate_list = Category.objects.filter(blog=blog)
        tags = Tag.objects.filter(blog=blog)
        dict = {
            "blog":blog,
            "cate_list":cate_list,
            "tags":tags,
        }

    return render(request, "backend/add_article.html",dict)

def upload(request):
    print(request.FILES)
    obj = request.FILES.get("upload_img")  # 獲取文件對象
    name = obj.name  # 文件名
    #文件存儲的絕對路徑
    path = os.path.join(settings.BASE_DIR, "static", "upload", name)
    with open(path, "wb") as f:
        for line in obj:  # 遍歷文件對象
            f.write(line)  # 寫入文件

    #必須返回這2個key
    res = {
        # 爲0表示沒有錯誤,若是有錯誤,設置爲1。增長一個key爲message,用來顯示指定的錯誤
        "error": 0,
        # 圖片訪問路徑,必須可以直接訪問到
        "url": "/static/upload/" + name
    }

    return HttpResponse(json.dumps(res))  # 必須返回Json
View Code

數據庫刪除剛纔那篇文章

刷新頁面,從新添加一篇文章

注意:第一行是一個script標籤

 

提交以後,查看這一篇文章

點擊文章標題

發現沒有彈框了,它將script標籤轉義了

 

做業:

複製代碼
1 系統首頁的文章渲染
2 我的站點的查詢和跳轉
3 文章詳情頁的渲染
4 點贊
5 處理根評論
6 後臺管理
    --- 首頁
    --- 添加文章
        ----利用BS模塊防護xss攻擊
        
        
延伸:
    1 基於Ajax的登錄
    2 基於AJax註冊功能(文件上傳)
      --- 預備:基於ajax上傳文件
複製代碼

 

答案:

完整代碼已上傳至github,地址以下:

https://github.com/py3study/cnblog

實現的功能以下

複製代碼
1. 註冊頁面 /zhuce/  基於ajax+form組件
2. 登陸頁面 /login/  基於ajax
3. 後臺頁面 /backend/
4. 後臺能實現文章、分類、標籤的增刪改查,部分功能基於ajax
5. 登陸以後,頁面右上角有進入後臺,註銷等功能
6. 不能直接訪問後臺頁面,必需要登陸。不然會有彈框提示!
複製代碼

後臺效果以下:

 


參考資料:

Django 基礎教程

轉載聲明:
做者:肖祥
出處: https://www.cnblogs.com/xiao987334176/

相關文章
相關標籤/搜索