文章评论业务完善

提交评论
评论框里面的内容会清空 然后页面会有一个临时评论样式出现 页面刷新才会出现评论楼样式 研究子评论特性
每个评论右侧都应该有回复按钮 点击就可以填写子评论
点击回复按钮具体动作:评论框中自动添加@+评论的人名并换行 聚焦
如何区分不同的回复按钮所对应的用户名
利用标签可以自定义属性直接携带对应的评论用户名即可 提交根评论和子评论点击的是同一个按钮 两者的区别与联系是什么
其实根评论和子评论的唯一区别就是是否有父评论的主键值
如何区分不同的回复按钮所对应的评论主键值
利用标签可以自定义属性直接携带对应的评论主键值即可 点击回复按钮发送子评论 页面不刷新的情况下 后续的评论全部成了子评论
原因是全局变量parentId没有清空导致的 每次提交评论都应该清空一下 针对子评论内中的@用户名换行 理论上不属于用户评论的内容 不应该记录到数据库
前端可以剔除 也可以在后端剔除 针对子评论的渲染 应该动态判断是否是子评论 如果是应该加上评论的目标用户名

注意:针对评论的渲染也可以分页 也可以做根评论与子评论的集合操作(分类)

后台管理

base.html

    <div class="container-fluid">
<div class="row">
<div class="col-md-2">
<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="false" aria-controls="collapseOne" class="collapsed">
博客后台
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="headingOne" aria-expanded="false" style="height: 0px;">
<div class="panel-body">
<a href="/add/">新建随笔</a>
</div>
<div class="panel-body">
<a href="">草稿箱</a>
</div>
<div class="panel-body">
<a href="">回收站</a>
</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" aria-expanded="false" style="height: 0px;">
<div class="panel-body">
<a href="">新增分类</a>
</div>
<div class="panel-body">
<a href="">分类列表<</a>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-10">
<div class="is-show">
<h4 class="is-show">文章展示</h4>
<ul class="nav nav-tabs">
<li role="presentation" class="active"><a href="#">文章</a></li>
<li role="presentation"><a href="#">新闻</a></li>
<li role="presentation"><a href="#">标签</a></li>
</ul> <div class="tab-content">
<div role="tabpanel" class="tab-pane fade in active" id="home">
{% block crticle %} {% endblock %}
</div> </div>
</div> {% block add %} {% endblock %}
</div> </div>
</div>

index.html

{% extends 'backend/base.html' %}
{% block title %}
后台管理
{% endblock %} {% block crticle %}
<div class="bs-example" data-example-id="hoverable-table">
<table class="table table-hover">
<thead>
<tr>
<th>编号</th>
<th>标题</th>
<th>发布时间</th>
<th>评论数</th>
<th>操作</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for article in article_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td><a href="/{{ article.blog.userinfo.username }}/articles/{{ article.id }}">{{ article.title }}/</a></td>
<td>{{ article.create_time|date:'Y-m-d H:i' }}</td>
<td>{{ article.comment_num }}</td>
<td><a href="/delete/?pk={{ article.id }}">删除</a></td>
<td><a href="/alter_article/?pk={{ article.id }}">修改</a></td>
</tr>
{% endfor %} </tbody>
</table>
</div>
{% endblock %}

新建文章

前端模板

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

{% block link %}
<script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
<script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
{% endblock %} {% block title %}
添加文章
{% endblock %} {% block add %}
<div class="text-center" style="background: #2aabd2">
<h3>添加随笔</h3>
</div>
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="add-title">标题</label>
<input type="text" id="add-title" name="title" class="form-control">
</div>
<div class="form-group">
<label for="add-content">内容</label>
<div>
<textarea name="content" id="editor_id" cols="300" rows="20"></textarea>
</div> </div> <div class="form-group">
<label for="add-classify">分类</label>
<select class="form-control" name="category" id="add-classify">
{% for classify in classify_list %}
<option value="{{ classify.id }}">{{ classify.name }}</option>
{% endfor %}
</select>
</div> <div class="form-group">
<label for="add-tag">标签</label>
<select class="form-control" name="tag" id="add-tag" multiple>
{% for tag in tag_list %}
<option value="{{ tag.id }}">{{ tag.name }}</option>
{% endfor %}
</select>
</div>
<button class="btn btn-success form-control">上传文章</button>
</form> {% endblock %} {% block js %}
// 使用富文本编辑器
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#editor_id', {
width: '100%',
height: '300px',
resizeType: '1',
// 上传图片相关
uploadJson: '/put_img/',
//filePostName: 'myfile', //默认imgFile
//extraFileUploadParams: {
// 'csrfmiddlewaretoken': '{{ csrf_token }}'
// } 后端没有取消校验 需要传csrf
});
});
</script>
{% endblock %}
def add(request):
if request.method == 'GET':
tag_list = Tag.objects.filter(blog=request.user.blog)
classify_list = Classify.objects.filter(blog=request.user.blog)
return render(request, 'backend/add.html', context={'tag_list': tag_list, 'classify_list': classify_list})
title = request.POST.get('title')
content = request.POST.get('content')
# BeautifulSoup第一个参数是html内容,第二个参数:使用的解析器
bs = BeautifulSoup(content, features='html.parser')
# 截取html文本,将空格和换行替换成空,并截取70个字符
desc = bs.text.replace(' ', '').replace('\n', '')[:70] + '...'
# 剔除script标签
script_list = bs.findAll('script')
for i in script_list:
i.decompose() # 将每个script标签删除
classify = request.POST.get('category')
tag = request.POST.getlist('tag') # 这是多对多的
res = Article.objects.create(title=title, content=str(bs), desc=desc, classify_id=classify, blog=request.user.blog)
# 多对多添加外键关系
res.tag.add(*tag)
return redirect('/backend/')

富文本编辑器图片处理,查看官方文档

# 文章图片处理
# 需要处理csrf 可已经用掉这个接口的csrf @csrf_exempt # 免除校验
def put_img(request):
img = request.FILES.get('imgFile')
path = os.path.join(settings.MEDIA_ROOT, 'upload', img.name)
with open(path, 'wb') as f:
for i in img:
f.write(i)
return JsonResponse({
"error": 0,
"url": f"http://127.0.0.1:8000/media/upload/{img.name}"
})

处理xss攻击

xss跨站脚本,在内容中存script脚本,前端渲染时使用了safe,如果存在script脚本,就会执行。解决方案。富文本编辑器在输入代码块时会自动将尖括号转换成对应的字符,只需在后端将恶意的script清除即可

  1. 页面简易搭建
  2. 文章内容区富文本编辑器的使用

    课下可以自行查找更多的富文本编辑器使用
  3. 添加文章需要注意的问题

    文章简介不应该有标签存在

    文章内容不允许编辑script脚本(XSS攻击)

涉及到html相关内容的处理 可以借助于爬虫相关模块

bs4

需要使用beautifulsoup4模块

-pip3 install beautifulsoup4
-删除script标签
soup = BeautifulSoup(content, 'html.parser')
script_list=soup.findAll('script') # 搜索到html中所有的script标签
for script in script_list:
script.decompose() # 把搜到的script标签一个个删除

首页用户信息展示

用户登陆后展示用户名和和管理选项按钮

<!--首页用户信息展示 未登录显示登录和注册-->
{% if request.user.is_authenticated %}
<li><a href="{{ request.user.username }}">{{ 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="/set_pwd/">修改密码</a></li>
<li><a href="/backend/">后台管理</a></li>
<li><a href="/alter_icon/">修改头像</a></li>
<li role="separator" class="divider"></li>
<li><a href="/login_out/">退出登录</a></li>
</ul>
</li>
{% else %}
<a href="/login/">登录</a>
<a href="/register/">注册</a>
{% endif %}

退出后台

# 退出登录
def login_out(request):
logout(request) # request.session.flush() 清除掉session和cookie
return redirect('/')

修改头像

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

{% block title %}
修改头像
{% endblock %} {% block add %}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div style="margin-top: 100px">
<h3 style="color: darkslateblue">修改头像</h3>
<label for="icon">
<img src="/media/{{ icon }}" alt="" width="100px" height="100px" id="img">
</label>
<input type="file" id="icon" style="display: none" name="icon">
<button class="btn btn-success">确认修改</button>
</div>
</form> {% endblock %} {% block js %}
<script>
$('.is-show').toggle() // 头像动态显示 给文件标签绑定一个变化事件
$('#icon').change(function () {
var reader = new FileReader() // 获取文件内容
var file = $('#icon')[0].files[0] reader.readAsDataURL(file) reader.onload = (function () {
$('#img').attr('src', reader.result)
}) })
</script>
{% endblock %}
# 修改头像
def alter_icon(request):
if request.method == "GET":
# 需要当前用户头像
icon = request.user.icon
return render(request, 'backend/alter_icon.html', context={'icon': icon})
icon = request.FILES.get('icon')
request.user.icon = icon
request.user.save()
return redirect('/')

修改密码

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

{% block title %}
修改密码
{% endblock %} {% block add %}
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="pwd1">原密码</label>
<input type="password" id="pwd1" name="old_password" class="form-control">
</div>
<div class="form-group">
<label for="pwd2">新密码</label>
<input type="password" id="pwd2" name="new_password" class="form-control">
</div>
<div class="form-group">
<label for="pwd3">确认密码</label>
<input type="password" id="pwd3" name="re_password" class="form-control">
</div>
<button class="form-control btn-success">提交</button> <span style="color: red">{{ error }}</span>
</form>
{% endblock %}
def set_pwd(request):
if request.method == 'GET':
return render(request, 'backend/set_pwd.html')
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
re_password = request.POST.get('re_password')
if request.user.check_password(old_password):
if new_password == re_password:
request.user.set_password(new_password)
request.user.save()
# 退出当前登录 跳转至登录
login_out(request)
return redirect(to='/login/')
return render(request, 'backend/set_pwd.html', context={'error': '两次密码不一致'})
return render(request, 'backend/set_pwd.html', context={'error': '原密码不一致'})

修改文章

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

{% block link %}
<script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
<script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
{% endblock %} {% block title %}
修改文章
{% endblock %} {% block add %}
<div class="text-center" style="background: #2aabd2">
<h3>修改文章</h3>
</div>
<form action="" method="post">
{% csrf_token %}
<div class="form-group">
<label for="add-title">标题</label>
<input type="text" id="add-title" name="title" class="form-control" value="{{ article.title }}">
</div>
<div class="form-group">
<label for="add-content">内容</label>
<div>
<textarea name="content" id="editor_id" cols="300" rows="20">{{ article.content }}</textarea>
</div> </div> <div class="form-group">
<label for="add-classify">分类</label>
<select class="form-control" name="category" id="add-classify">
{% for classify in classify_list %}
{% if classify == article.classify %}
<option value="{{ classify.id }}" selected>{{ classify.name }}</option>
{% else %}
<option value="{{ classify.id }}">{{ classify.name }}</option>
{% endif %} {% endfor %}
</select>
</div> <div class="form-group">
<label for="add-tag">标签</label>
<select class="form-control" name="tag" id="add-tag" multiple>
{% for tag in tag_list %}
{% if tag in tag_list %}
<option value="{{ tag.id }}" selected>{{ tag.name }}</option>
{% else %}
<option value="{{ tag.id }}">{{ tag.name }}</option>
{% endif %} {% endfor %}
</select>
</div>
<button class="btn btn-success form-control">上传文章</button>
</form> {% endblock %} {% block js %}
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#editor_id', {
width: '100%',
height: '300px',
resizeType: '1',
// 上传图片相关
uploadJson: '/put_img/',
//filePostName: 'myfile', //默认imgFile
//extraFileUploadParams: {
// 'csrfmiddlewaretoken': '{{ csrf_token }}'
// } 后端没有取消校验 需要传csrf
});
});
</script>
{% endblock %}
def alter_article(request):
pk = request.GET.get('pk')
# 需要当前文章 当前用户的分类和标签
if request.method == 'GET':
article = Article.objects.filter(pk=pk).first()
classify_list = Classify.objects.filter(blog=request.user.blog)
tag_list = Tag.objects.filter(blog=request.user.blog)
return render(request, 'backend/alter_article.html',
context={'article': article, 'classify_list': classify_list, 'tag_list': tag_list})
# post请求 修改文章
title = request.POST.get('title')
content = request.POST.get('content')
# BeautifulSoup第一个参数是html内容,第二个参数:使用的解析器
bs = BeautifulSoup(content, features='html.parser')
# 截取html文本,将空格和换行替换成空,并截取70个字符
desc = bs.text.replace(' ', '').replace('\n', '')[:70] + '...'
# 剔除script标签
script_list = bs.findAll('script')
for i in script_list:
i.decompose() # 将每个script标签删除
classify = request.POST.get('category')
tag = request.POST.getlist('tag') # 这是多对多的
article = Article.objects.filter(pk=request.GET.get('pk')) # 必须是一个queryset
# 还需要将该文章的评论点赞点踩一起更新
up_num = Article.objects.filter(pk=pk).first().up_num
down_num = Article.objects.filter(pk=pk).first().down_num
comment_num = Article.objects.filter(pk=pk).first().comment_num
with transaction.atomic():
article.update(title=title, desc=desc, classify_id=classify, content=str(bs), blog=request.user.blog,
up_num=up_num, down_num=down_num, comment_num=comment_num)
article.first().save()
# 多对多关系添加
article.first().tag.set(tag)
return redirect(f'/{request.user.username}/articles/{pk}')

bbs项目(部分讲解)的更多相关文章

  1. BBS项目详解(forms快速创建登陆页面,登陆验证、通过阅读器进行头像上传的预览、内存管理器)

    BBS项目涉及的知识点 django中知识点 钩子函数(局部钩子和全局钩子) 1.局部钩子就是用来做合法性校验,比如用户名有没有被使用等 2.全局的就是用来做对比校验,比如两次输入的密码是否一致 3. ...

  2. BBS项目部署

    1.准备 项目架构为:LNM+Python+Django+uwsgi+Redis   (L:linux,N:nginx,M:mysql) 将bbs项目压缩上传到:  /opt 在shell中直接拖拽 ...

  3. auth复习和BBS项目的登录(1)

    auth复习 auth组件 验证:authenticate(request,username='andy',password='123) 登录:login(request,user) 注销:login ...

  4. python 自动化之路 day 20 Django进阶/BBS项目【一】

    一.django进阶 1.django orm 增删改查 1.1.创建表: 1 2 3 >>> from blog.models import Blog >>> b ...

  5. BBS项目知识点汇总

    目录 bbs项目知识点汇总 一. JavaScript 1 替换头像 2 form表单拿数据 3 form组件error信息渲染 4 添加html代码 5 聚焦操作 二 . html在线编辑器 三 . ...

  6. BBS项目-01

    目录 BBS项目 BBS开发流程: BBS表格创建: BBS项目 BBS开发流程: BBS项目: 开发流程: 需求分析 草拟一些项目的大致技术点和流程 架构设计 架构师(框架 语言 数据库 缓存数据库 ...

  7. 小福bbs—项目系统设计与数据库设计

    这个作业属于哪个课程 班级链接 这个作业要求在哪里 作业要求的链接 团队名称 小福bbs 这个作业的目标 实现对校园论坛软件的制作,使其能够发布帖子,查看信息等 作业的正文 小福bbs--项目需求分析 ...

  8. 小福bbs——项目需求分析

    # 一.简单了解 这个作业属于哪个课程 班级链接 这个作业要求在哪里 作业要求的链接 团队名称 小福bbs 这个作业的目标 第一个版本,根据项目预期情况形成 作业的正文 小福bbs--项目需求分析 其 ...

  9. day75 bbs项目☞后台管理+修改头像

    目录 一.后台管理之添加文章 二.修改用户头像 bbs项目总结 一.后台管理之添加文章 添加文章有两个需要注意的问题: 文章的简介切取,应该想办法获取到当前文章的文本内容后再截取字符 XSS攻击,由于 ...

  10. IDEA 新建 Java 项目 (图文讲解, 良心教程)

    IDEA 新建 Java 项目 (图文讲解, 良心教程) 欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章, 关注回复「资源」, 免费领取全网最热的Java架构师学习PDF, 转载 ...

随机推荐

  1. JUC(8)JMM

    文章目录 1.JMM 2.volatile 3.单例模式 1.JMM Volatile是java虚拟机提供轻量级的同步机制 1.保证可见性 2.不保证原子性 3.禁止指令重排 什么是JMM java内 ...

  2. Windows7下驱动开发与调试体系构建——0.概述

    本文集内容为windows7x64下驱动开发与调试体系构建,内容目录如下: 1.驱动开发的环境准备 2.R3与R0的通信示例 3.自建调试体系概述 4.在x64下使用汇编代码 5.实战反调试标记位(N ...

  3. Python基础部分:10、数据类型的内置方法和字符编码

    目录 一.数据类型内置方法 1.字典dict内置方法 1.1.类型转换 2.字典必须要掌握的方法 2.1.取值方式 2.2.修改内部数据值 2.3.删除数据 2.4.统计字典中键值对个数 2.5.字典 ...

  4. Docker容器化技术

    1. 初始Docker 1.1 Docker概念 Docker概念:Docker是一个开源的应用容器引擎 诞生于2013年初,基于Go实现,dotCloud公司出品(后改名为Docker Inc) D ...

  5. 畅联新增插件:新增依爱NB烟感

    双美接入,C++版,就是解析Json时稍微有点问题,依爱这边的Json格式,Value字段竟然是单引号....呵呵,反正也解决了. 备注一下:电信模式,AEP平台模式.

  6. 【Virt.Contest】CF1321(div.2)

    第一次打虚拟赛. CF 传送门 T1:Contest for Robots 统计 \(r[i]=1\) 且 \(b[i]=0\) 的位数 \(t1\) 和 \(r[i]=0\) 且 \(b[i]=1\ ...

  7. Ajax基础(上)

    当我们在浏览器地址栏中输入一个网址,或者通过网页表单向服务器提交内容的时候,我们就开始与服务器进行交互. 传统的Web应用交互: (1)用户触发一个Http请求到服务器,服务器对其进行处理后再返回一个 ...

  8. Inventor 2021保姆级安装教程

    Inventor 2021 WIN10 64位安装步骤: 1.先使用"百度网盘客户端"下载INT21_CN_x64安装包到电脑磁盘里,并鼠标右击进行解压缩,安装前先断网,然后找到I ...

  9. 基于SqlSugar的开发框架循序渐进介绍(22)-- Vue3+TypeScript的前端工作流模块中实现统一的表单编辑和表单详情查看处理

    在工作流页面中,除了特定的业务表单信息外,往往也需要同时展示通用申请单的相关信息,因此在页面设计的时候需要使用一些组件化的概念来实现动态的内容展示处理,本篇随笔介绍Vue3+TypeScript+El ...

  10. Swing常用窗体

    Swing常用窗体 Swing 主要具有以下特点: ( 1 )轻量级组件. ( 2 )可插入外观组件. 窗体作为Swing的应用程序中组件的承载体,处于非常重要的地位.Swing中常用的窗体包括JFr ...