Django学习 day76之BBS项目final day

根评论功能

es6语法补充

js中 es6语法,反引号的用法:

var content='sb'  // 定义一个js的变量
`egon is ${content}`  // 在反引号包裹的字符串可以这样将变量代入

前端

评论列表

<!--在这之上的点赞点踩会发生浮动塌陷,所以要在其外包裹一层清除浮动<div class="clearfix">...</div>-->
<hr>
<div>
    评论列表
    <ul class="list-group">
        {% for comment in comment_list %}
            <li class="list-group-item">
                <div>
                    <span>#{{ forloop.counter }}楼</span>
                    <span>{{ comment.create_time|date:'Y-m-d H-i-s' }}</span>
                    <span><a href="/{{ comment.user.username }}">{{ comment.user.username }}</a></span>
                    <span class="pull-right id_replay" username="{{ comment.user.username }} "
                          parent="{{ comment.pk }}"><a>回复</a></span>
                </div>
                <hr>
                <div>
                    {% if comment.commit_id_id %}
                        <p>@{{ comment.commit_id.user.username }}</p>
                        <p>{{ comment.content }}</p>
                    {% else %}
                        {{ comment.content }}
                    {% endif %}
                </div>
            </li>
        {% endfor %}
    </ul>
</div>

评论框

{% if request.user.is_authenticated %}
    <div>
        <p class="glyphicon glyphicon-copyright-mark">发表评论</p>
        <p><textarea name="" id="id_text" cols="180" rows="10"></textarea></p>
        <p>
            <button class="btn btn-success" id="id_comment">发表评论</button>
        </p>
    </div>
{% else %}
    <div>
        登录后才能发表评论,立即 <a href="/login/">登录</a><a href="/register/">注册</a>, 访问 网站首页
    </div>
{% endif %}

js代码

$('#id_comment').click(function () {
    let content = $('#id_text').val()
    $.ajax({
        url: '/comment/',
        method: 'post',
        data: {
            article_id: '{{ article.id }}',
            content: content,
            parent: parent_id,
            csrfmiddlewaretoken: '{{ csrf_token }}'
        },
        success: function (data) {
            if (data.code == 100) {
                let username = data.username
                let res_content = data.content
                let ss = ``
                ss = `<li class="list-group-item">
                        <div>
                            <span class="glyphicon glyphicon-comment">${username</span>
                        </div>
                        <div>
                        <p>@${parent_name}</p>
                            ${res_content}
                        </div>
                    </li>`
              
                //清空输入框
                $('#id_text').val('')
                //把ss追加到评论列表的后面,这只是一个假评论,只有自己能看到,没有楼层标识,只是为了表明评论成功,只有在刷新页面后才能看到带有楼层的评论
                $('.list-group').append(ss)
            }
        }
    })
})

后端(包含下一小节的子评论代码)

def comment(request):
    res = {'code': 100, 'msg': ''}
    if request.is_ajax():
        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent = request.POST.get('parent')
        if request.user.is_authenticated:
        	# 下面的两个建字段最好放一个事务里,这里懒得写
            article = models.Commit.objects.create(user=request.user, article_id=article_id, content=content,
                                                   commit_id_id=parent)
            models.Article.objects.filter(pk=article_id).update(commit_num=F('commit_num') + 1)
            res['msg'] = '评论成功'
            res['username'] = article.user.username
            res['content'] = article.content
            if parent:
                res['parent_name'] = article.commit_id.user.username

        else:
            res['code'] = 109
            res['msg'] = '请先登录'

    return JsonResponse(res)

二 子评论功能

后端同上

前端js:


$('#id_comment').click(function () {

    let content = $('#id_text').val()
    if (parent_id) {
        //这表示子评论
        //取到字符串第一个\n的位置索引,是一个数字
        let i = content.indexOf('\n') + 1
        //从 \n+1的位置开始截取,截取到最后
        content = content.slice(i)
    }
    $.ajax({
        url: '/comment/',
        method: 'post',
        data: {
            article_id: '{{ article.id }}',
            content: content,
            parent: parent_id,
            csrfmiddlewaretoken: '{{ csrf_token }}'
        },
        success: function (data) {
            console.log(data)

            if (data.code == 100) {

                let username = data.username
                let res_content = data.content
                let parent_name = data.parent_name

                let ss = ``
                if (parent_id) {
                    ss = `<li class="list-group-item">
                        <div>
                            <span class="glyphicon glyphicon-comment">${username}</span>
                        </div>
                        <div>
                        <p>@${parent_name}</p>
                            ${res_content}
                        </div>
                    </li>`
                } else {
                    ss = `<li class="list-group-item">
                        <div>
                            <span class="glyphicon glyphicon-comment">${username}</span>
                        </div>
                        <div>
                            ${res_content}
                        </div>
                    </li>`
                }

                //清空输入框
                $('#id_text').val('')
                //把ss追加到评论列表的后面
                $('.list-group').append(ss)
                //把parent_id置空
                parent_id = ''
            }
        }
    })
})

$('.id_replay').click(function () {
    let username = $(this).attr('username')
    parent_id = $(this).attr('parent')

    $('#id_text').val('@' + username + '\n').focus()
})

三 后台管理页面搭建

backend/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>后台管理</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/font-awesome-4.7.0/css/font-awesome.css">
    <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
    <script src="/static/bootstrap/js/bootstrap.min.js"></script>
    <style>

    </style>
</head>
<body>
<div>
    <div class="header">
        <nav class="navbar navbar-default navbar-inverse">
            <div class="container-fluid">
                <div class="navbar-header">
                    <a class="navbar-brand" href="#">后台管理</a>
                </div>

                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="/index/">首页 <span class="sr-only">(current)</span></a></li>

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

        <div class="row">
            <div class="left_content col-md-3">
                <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                    <div class="panel panel-default">
                        <div class="panel-heading" role="tab" id="headingOne">
                            <h4 class="panel-title">
                                <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
                                   aria-expanded="true" aria-controls="collapseOne">
                                    操作
                                </a>
                            </h4>
                        </div>
                        <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                             aria-labelledby="headingOne">
                            <div class="panel-body">
                                <ul class="nav">
                                    <li><a href="/new_article/">新增文章</a></li>
                                    <li><a href="">新增随笔</a></li>
                                </ul>

                            </div>
                        </div>
                    </div>
                    <div class="panel panel-default">
                        <div class="panel-heading" role="tab" id="headingTwo">
                            <h4 class="panel-title">
                                <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion"
                                   href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
                                    分类
                                </a>
                            </h4>
                        </div>
                        <div id="collapseTwo" class="panel-collapse collapse" role="tabpanel"
                             aria-labelledby="headingTwo">
                            <div class="panel-body">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="right_content col-md-9">
                <div>

                    <!-- Nav tabs -->
                    <ul class="nav nav-tabs" role="tablist">
                        <li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab"
                                                                  data-toggle="tab">文章</a></li>
                        <li role="presentation"><a href="#profile" aria-controls="profile" role="tab"
                                                   data-toggle="tab">评论</a>
                        </li>
                        <li role="presentation"><a href="#messages" aria-controls="messages" role="tab"
                                                   data-toggle="tab">日志</a>
                        </li>
                        <li role="presentation"><a href="#settings" aria-controls="settings" role="tab"
                                                   data-toggle="tab">标签</a>
                        </li>
                    </ul>

                    <!-- Tab panes -->
                    <div class="tab-content">
                        <div role="tabpanel" class="tab-pane active" id="home">
                            {% block content %}

                            {% endblock %}
                        </div>
                        <div role="tabpanel" class="tab-pane" id="profile">
                            评论的内容
                        </div>
                        <div role="tabpanel" class="tab-pane" id="messages">
                            日志的内容
                        </div>
                        <div role="tabpanel" class="tab-pane" id="settings">
                            标签的内容
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
{% block script %}

{% endblock %}
</html>

backend/backend_index.html

{% extends 'backend/base.html' %}
{% block content %}
    <table class="table table-hover table-striped">
    <thead>
    <tr>
        <th>标题</th>
        <th>评论数</th>
        <th>点赞数</th>
        <th>操作</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody >
        {% for article in article_list %}
            <tr>
            <td><a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}.html">{{ article.title }}</a></td>
            <td>{{ article.commit_num }}</td>
            <td>{{ article.up_num }}</td>
            <td><a href="">编辑</a></td>
            <td><a href="">删除</a></td>
            </tr>
        {% endfor %}

    </tbody>
    </table>
{% endblock %}

后台

@login_required(login_url='/login/')
def backend_index(request):
    article_list = models.Article.objects.filter(blog=request.user.blog)
    return render(request, 'backend/backend_index.html', locals())

四 富文本编辑器使用

  1. 下载kindeditor:http://kindeditor.net/down.php
  2. 解压缩,把整个文件加放入static目录下
  3. add_article.html
    <div class="form-group">
    <label for="">内容</label>
    <textarea name="content" id="editor_id" cols="80" rows="10" class="form-control">	 </textarea>
    </div>
    
  4. js代码
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '500px',
                resizeType: 1,
            });
        });
    

五 新增文章,处理xss攻击

# pip3 install beautifulsoup4
from bs4 import BeautifulSoup

@login_required(login_url='/login/')
def new_article(request):
    if request.method == 'GET':
        category_list = models.Category.objects.filter(blog=request.user.blog)
        tag_list = models.Tag.objects.filter(blog=request.user.blog)
        return render(request, 'backend/add_article.html', locals())
    else:
        title = request.POST.get('title')
        content = request.POST.get('content')
        category = request.POST.get('category')
        tags = request.POST.getlist('tag')
        # desc = content[0:90] # 会带着标签
        soup = BeautifulSoup(content,'html.parser')
        desc=soup.text[0:90]  # 去掉所有标签后的文本
        # 把script标签干掉
        res_script=soup.find_all('script')
        for script in res_script:
            script.decompose() # 删除script标签

        article = models.Article.objects.create(title=title, content=str(soup), desc=desc, blog=request.user.blog,
                                                category_id=category)

        # 存tag(自动生成第三张表)
        # article.tag.add(*tag)
        # 手动做
        # for tag in tags:
        #     models.TagToArticle.objects.create(article_id=article.pk,tag_id=tag)

        # 批量插入
        ll=[]
        for tag in tags:
            ll.append(models.TagToArticle(article_id=article.pk,tag_id=tag))
        models.TagToArticle.objects.bulk_create(ll)

        return redirect('/backend_index/')

六 富文本编辑器上传图片

js

 KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '500px',
                resizeType: 1,
                uploadJson:'/upload_img/',
                filePostName:'myfile',
                //额外带的参数
                extraFileUploadParams : {
                      csrfmiddlewaretoken: '{{ csrf_token }}',
                }
            });
        });

后端

def upload_img(request):
    res={'error':0}
    print(request.FILES)
    try:
        # 存图片
        file=request.FILES.get('myfile')
        path=os.path.join(settings.BASE_DIR,'media','img',file.name)
        with open(path,'wb') as f:
            for line in file:
                f.write(line)
        res['url']='/media/img/'+file.name
    except Exception as e:
        res['error']=1
        res['message']=str(e)
    return JsonResponse(res)

七 修改头像

后台

@login_required(login_url='/login/')
def update_head(request):
    if request.method=='GET':
        return render(request,'backend/update_head.html')
    else:
        head=request.FILES.get('myfile')
        # 方式一
        request.user.avatar=head
        request.user.save()
        return redirect('/')
        # 方式二(不可以,它不能自动添加上avatar/前缀)
        # models.UserInfo.objects.filter(pk=request.user.id).update(avatar=head)
        # return redirect('/')

前端

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

{% block head %}
    <form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
        <div class="form-group">
            <label for="">原头像</label>
            <img src="/media/{{ request.user.avatar }}" height="80" width="80">
        </div>
        <div class="form-group">
            <label for="id_myfile">头像
                <img src="/static/img/default.png" alt="" id="id_img" height="80" width="80"
                     style="margin-left: 10px">
            </label>

            <input type="file" accept="image/*" name="myfile" id="id_myfile" style="display: none">
        </div>

        <div class="text-center">
            <input type="submit" value="修改" id="id_submit" class="btn btn-danger "><span
                class="error text-danger" style="margin-left: 10px"></span>
        </div>
    </form>
{% endblock %}


{% block script %}

    <script>
        $("#id_myfile").change(function () {
            //借助于文件阅读器
            let filereader = new FileReader()
            //把图片读到filereader对象中
            //$('#id_myfile')[0].files[0]
            filereader.readAsDataURL($('#id_myfile')[0].files[0])
            //$('#id_img').attr('src','https://account.cnblogs.com/images/registersideimg.png')
            //$('#id_img').attr('src',filereader.result) //这样不行,文件没读完
            filereader.onload = function () {
                //文件完全读到文件阅读器以后,再执行
                $('#id_img').attr('src', filereader.result)
            }
        })
    </script>
{% endblock %}

八 修改密码

前端

$('#btn_pwd_submit').click(function () {
    $(this).prop('disabled', true)
    $.ajax({
        url: '/update_pwd/',
        method: 'post',
        data: {
            pwd: $('#id_new_pwd').val(),
            csrfmiddlewaretoken: '{{ csrf_token }}',
        },
        success: function (data) {

            $('#btn_pwd_submit').prop('disabled', false)
            $('#id_alert').removeClass('hidden').children('strong').html(data.msg)
            setTimeout(function () {
                $('#id_alert').addClass('hidden')
            },3000)
            if (data.code == 100) {
                //模态框销毁
                $('#myModal').modal('hide')
            }
        }
    })
})

后端

def update_pwd(request):
    res = {'code': 100, 'msg': '密码修改成功'}
    try:
        pwd = request.POST.get('pwd')
        print(pwd)
        request.user.set_password(pwd)
        request.user.save()
    except Exception:
        res['code'] = 101
        res['msg'] = '密码修改失败'
    return JsonResponse(res)

九 删除文件,修改文章

给我自己实现!没想到吧

十 邮件通知(django中发送邮件)

评论成功发送邮件

django现成的函数,可以直接发送邮件

  1. setting中配置
    EMAIL_HOST = 'smtp.qq.com' # 如果是 163 改成 smtp.163.com 以什么邮箱发送
    EMAIL_PORT = 465 # 端口号
    EMAIL_HOST_USER = '306334678@qq.com' # 帐号 发送者邮箱账号
    EMAIL_HOST_PASSWORD = '' # 密码 不是密码, 授权码
    DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
    EMAIL_USE_SSL = True #使用ssl,qq只支持这种

  2. 视图函数中使用

    from django.core.mail import send_mail
    send_mail('您的文章:%s被评论了'%'git从入门到放弃','%s评论了%s'%(request.user.username,'写的真好'),settings.EMAIL_HOST_USER,                                    ["1063926627@qq.com",'laichuangdelaoji@gmail.com'])
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值