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())
四 富文本编辑器使用
- 下载
kindeditor:http://kindeditor.net/down.php
- 解压缩,把整个文件加放入static目录下
- 在
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>
- 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现成的函数,可以直接发送邮件
-
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只支持这种 -
视图函数中使用
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'])