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, 转载 ...
随机推荐
- 【JavaScript排序】 sort()方法(解决null、undefined、0之间的排序(混乱)问题)
JavaScript排序 - sort()方法 --解决null.undefined.0之间的排序(混乱)问题 一.普通的数组排序 JavaScript中用方法sort()为数组排序.sort() ...
- 【C++】GoogleTest进阶之gMock
gMock是什么 当我们去写测试时,有些测试对象很单纯简单,例如一个函数完全不依赖于其他的对象,那么就只需要验证其输入输出是否符合预期即可. 但是如果测试对象很复杂或者依赖于其他的对象呢?例如一个函数 ...
- How to get the return value of the setTimeout inner function in js All In One
How to get the return value of the setTimeout inner function in js All In One 在 js 中如何获取 setTimeout ...
- 2、yaml配置文件当中的坑(数字的定义和支持进制书写格式)
6.进制数的转换 6.1.问题回顾 我记得我刚刚开始使用SpringBoot的时候,有一天在做到SpringBoot整合第三方技术的时候 我刚好在那天学习到整合Mybatis,做Web项目嘛,不连数据 ...
- python基础之数据类型总结
一.列表 1.作用:列表主要用于存储多个数据. 2.空列表表示:li=[]或者li=list() 3.列表的索引和切片:同字符串的索引和切片,索引超出范围报错,切片超出范围不报错. list3 = [ ...
- C语言二叉树遍历及路径查找
#include<iostream> #include<stdio.h> #include<math.h> #include<malloc.h> usi ...
- 优雅的写好Vue项目代码 — 路由拆分、Vuex模块拆分、element按需加载
目录 路由的拆分 VUEX模块拆分 Element UI库按需加载的优雅写法 路由的拆分 项目较大路由较多时,路由拆分是一个不错的代码优化方案,按不同业务分为多个模块,结构清晰便于统一管理. requ ...
- vue-axios删除操作
<template> <div class="nav"> <input v-model="location" type=" ...
- 面试 个人摸底监测 考察JavaScript基础 (第三天)
01,如何开启JS严格模式?JS严格模式有什么特点? 两种方式 全局开启在js开头加上 'use strict' 局部开启,在作用域开头加上 function fn(){ 'use strict' } ...
- 「工具推荐」golang 代码可视化工具 go-callvis
「工具推荐」go-callvis go-callvis是相对 以图片的形式展示了go程序的调用关系,这个工具在看复杂项目时尤其有用. 亲测,借助它看祖传golang代码,头痛好多了. 安装 go ge ...