bbs项目(部分讲解)
文章评论业务完善
提交评论
评论框里面的内容会清空 然后页面会有一个临时评论样式出现 页面刷新才会出现评论楼样式
研究子评论特性
每个评论右侧都应该有回复按钮 点击就可以填写子评论
点击回复按钮具体动作:评论框中自动添加@+评论的人名并换行 聚焦
如何区分不同的回复按钮所对应的用户名
利用标签可以自定义属性直接携带对应的评论用户名即可
提交根评论和子评论点击的是同一个按钮 两者的区别与联系是什么
其实根评论和子评论的唯一区别就是是否有父评论的主键值
如何区分不同的回复按钮所对应的评论主键值
利用标签可以自定义属性直接携带对应的评论主键值即可
点击回复按钮发送子评论 页面不刷新的情况下 后续的评论全部成了子评论
原因是全局变量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
清除即可
- 页面简易搭建
- 文章内容区富文本编辑器的使用
课下可以自行查找更多的富文本编辑器使用 - 添加文章需要注意的问题
文章简介不应该有标签存在
文章内容不允许编辑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项目(部分讲解)的更多相关文章
- BBS项目详解(forms快速创建登陆页面,登陆验证、通过阅读器进行头像上传的预览、内存管理器)
BBS项目涉及的知识点 django中知识点 钩子函数(局部钩子和全局钩子) 1.局部钩子就是用来做合法性校验,比如用户名有没有被使用等 2.全局的就是用来做对比校验,比如两次输入的密码是否一致 3. ...
- BBS项目部署
1.准备 项目架构为:LNM+Python+Django+uwsgi+Redis (L:linux,N:nginx,M:mysql) 将bbs项目压缩上传到: /opt 在shell中直接拖拽 ...
- auth复习和BBS项目的登录(1)
auth复习 auth组件 验证:authenticate(request,username='andy',password='123) 登录:login(request,user) 注销:login ...
- python 自动化之路 day 20 Django进阶/BBS项目【一】
一.django进阶 1.django orm 增删改查 1.1.创建表: 1 2 3 >>> from blog.models import Blog >>> b ...
- BBS项目知识点汇总
目录 bbs项目知识点汇总 一. JavaScript 1 替换头像 2 form表单拿数据 3 form组件error信息渲染 4 添加html代码 5 聚焦操作 二 . html在线编辑器 三 . ...
- BBS项目-01
目录 BBS项目 BBS开发流程: BBS表格创建: BBS项目 BBS开发流程: BBS项目: 开发流程: 需求分析 草拟一些项目的大致技术点和流程 架构设计 架构师(框架 语言 数据库 缓存数据库 ...
- 小福bbs—项目系统设计与数据库设计
这个作业属于哪个课程 班级链接 这个作业要求在哪里 作业要求的链接 团队名称 小福bbs 这个作业的目标 实现对校园论坛软件的制作,使其能够发布帖子,查看信息等 作业的正文 小福bbs--项目需求分析 ...
- 小福bbs——项目需求分析
# 一.简单了解 这个作业属于哪个课程 班级链接 这个作业要求在哪里 作业要求的链接 团队名称 小福bbs 这个作业的目标 第一个版本,根据项目预期情况形成 作业的正文 小福bbs--项目需求分析 其 ...
- day75 bbs项目☞后台管理+修改头像
目录 一.后台管理之添加文章 二.修改用户头像 bbs项目总结 一.后台管理之添加文章 添加文章有两个需要注意的问题: 文章的简介切取,应该想办法获取到当前文章的文本内容后再截取字符 XSS攻击,由于 ...
- IDEA 新建 Java 项目 (图文讲解, 良心教程)
IDEA 新建 Java 项目 (图文讲解, 良心教程) 欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章, 关注回复「资源」, 免费领取全网最热的Java架构师学习PDF, 转载 ...
随机推荐
- 快速上手Mybatis项目
快速上手Mybatis项目 思路流程:搭建环境-->导入Mybatis--->编写代码--->测试 1.搭建实验数据库 CREATE DATABASE `mybatis`; USE ...
- MyBatisPlus分页插件在SpringBoot中的使用
文章目录 1.目录结构 2.新增配置 3.编写测试类 4.测试结果 5.数据库中的表 文件的创建: https://blog.csdn.net/weixin_43304253/article/deta ...
- .net core-利用PdfSharpCore和SkiaSharp.QrCode 添加PDF二维码页眉
前序 由于去年的一个项目需要在PDF 添加公司二维码 ,当时在网上找了很多操作PDF方案,第一种Aspose.PDF,很遗憾 Aspose.PDF 有添加版权的背景还是页脚我忘记了,不适合公司项目,最 ...
- 【pytest官方文档】解读- 开发可pip安装的第三方插件
在上一篇的 hooks 函数分享中,开发了一个本地插件示例,其实已经算是在编写插件了.今天继续跟着官方文档学习更多知识点. 一个插件包含一个或多个钩子函数,pytest 正是通过调用各种钩子组成的插件 ...
- 使用 StringUtils.split 的坑
点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 在日常的 Java 开发中,由于 J ...
- P6492 STEP(线段树维护左右区间pushup)
题目链接 题目描述: 给定一个长度为\(~\)n\(~\)的字符序列\(~\)a,初始时序列中全部都是字符\(~\)L. 有\(~\)q\(~\)次修改,每次给定一个\(~\)x,做出如下变化: \( ...
- linux 2021
常用安装 sudo apt install openssh-server # 安装ssh service ssh # 查看ssh服务的 基础 Debian 是Ubuntu的母板,有强大的包管理功能,使 ...
- springcloud组件梳理之hystrix
在微服务架构体系中,各服务中间的相互调用是常态,没有哪个服务能保证自身百分百不会出问题,然后再加上网络的波动以及环境等问题,服务间调用的稳定性无法保证,这时候就需要一个有容错能力的组件来介入,当调用出 ...
- java学习之springboot
0x00前言 呀呀呀时隔好久我又来做笔记了,上个月去大型保密活动了,这里在网上看了一些教程如果说不是去做java开发我就不做ssm的手动整合了采用springboot去一并开发. Spring Boo ...
- 安装harbor仓库
1.安装docker-compose curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-c ...