BBS项目 day05 后台系统功能(首页、文章列表展示、添加文章、上传文件(uuid生成新的文件名))

news/2024/11/17 8:48:34/

一、后台功能的初始配置

1. urls.py路由分发

    re_path('app02/', include('app02.urls')),

2.app02/urls.py

from django.urls import path, re_path, include
from app02 import viewsurlpatterns = [path('home/', views.home),path('article_list/', views.article_list),path('add_article/', views.add_article),path('upload_image/', views.upload_image),
]

二、后台功能之首页

1.首页前端

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>{% load static %}<script src="{% static 'js/jquery.min.js' %}"></script><link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet"><script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script><script src="{% static 'layer/layer.js' %}"></script></head>
<body>{# 导航条开始 #}
<nav class="navbar navbar-inverse"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse"data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">BBS博客园后台系统</a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="#">文章 <span class="sr-only">(current)</span></a></li><li><a href="#">分类</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="#">Action</a></li><li><a href="#">Another action</a></li><li><a href="#">Something else here</a></li><li role="separator" class="divider"></li><li><a href="#">Separated link</a></li><li role="separator" class="divider"></li><li><a href="#">One more separated link</a></li></ul></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="Search"></div><button type="submit" class="btn btn-default">搜索</button></form><ul class="nav navbar-nav navbar-right">{% if request.session.username %}<li style="line-height: 50px;"><!-- /media/{ article.blog.userinfo.avatar }} -->{# <img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" style="width: 100px;" alt="..."> #}<img src="/media/{{ cur_avatar }}" style="width: 50px; height: 36px;" class="onImg" alt=""></li><li><a href="#">{{ request.session.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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li><li><a href="#">更改头像</a></li><li><a href="/logout/">退出登录</a></li><li><a href="#">后台管理</a></li></ul></li>{% else %}<li><a href="/login/">登录</a></li><li><a href="/register/">注册</a></li>{% endif %}</ul><!-- 模态框 --><div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel"><div class="modal-dialog modal-lg" role="document"><div class="modal-content"><div class="row"><h1 class="text-center">修改密码</h1><div class="col-md-8 col-md-offset-2"><div class="form-group">用户名:<input type="text" readonly value="{{ request.session.username }}"class="form-control"></div><div class="form-group">原密码:<input type="password" id="old_password" class="form-control" msg="原密码必须输入"></div><div class="form-group">新密码:<input type="password" id="new_password" class="form-control" msg="原密码必须输入"></div><div class="form-group">确认密码:<input type="password" id="re_password" class="form-control" msg="原密码必须输入"></div><div class="form-group"><input type="button" value="修改密码" class="btn btn-primary btn-block btn_password"></div></div></div></div></div></div></div><!-- /.navbar-collapse --></div><!-- /.container-fluid -->
</nav>
{# 导航条结束 #}<div class="container-fluid"><div class="row"><div class="col-md-3"><div class="list-group"><a href="#" class="list-group-item active">首页</a><a href="/app02/article_list/" class="list-group-item">文章列表</a><a href="#" class="list-group-item">分类类别</a><a href="#" class="list-group-item">标签列表</a><a href="#" class="list-group-item">更多</a></div></div><div class="col-md-9"><div class="panel panel-info"><div class="panel-heading">我自一口真气足</div><div class="panel-body">{% block content %}<div class="jumbotron"><h1>最牛叉的博客平台</h1><p>无招胜有招</p><p><a class="btn btn-primary btn-lg" href="#" role="button">更过风景</a></p></div><div class="row"><div class="col-sm-6 col-md-4"><div class="thumbnail"><img src="https://img2.baidu.com/it/u=3323311628,2330835932&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1692464400&t=3cf590e7ea88465e48ef5170f7c70884"alt="..."><div class="caption"><h3>Thumbnail label</h3><p>清风拂山岗</p><p><a href="#" class="btn btn-primary" role="button">Button</a> <a href="#"class="btn btn-default"role="button">如来神掌</a></p></div></div></div><div class="col-sm-6 col-md-4"><div class="thumbnail"><img src="https://img2.baidu.com/it/u=3323311628,2330835932&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1692464400&t=3cf590e7ea88465e48ef5170f7c70884"alt="..."><div class="caption"><h3>Thumbnail label</h3><p>清风拂山岗</p><p><a href="#" class="btn btn-primary" role="button">Button</a> <a href="#"class="btn btn-default"role="button">如来神掌</a></p></div></div></div><div class="col-sm-6 col-md-4"><div class="thumbnail"><img src="https://img2.baidu.com/it/u=3323311628,2330835932&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1692464400&t=3cf590e7ea88465e48ef5170f7c70884"alt="..."><div class="caption"><h3>Thumbnail label</h3><p>清风拂山岗</p><p><a href="#" class="btn btn-primary" role="button">Button</a> <a href="#"class="btn btn-default"role="button">如来神掌</a></p></div></div></div></div>{% endblock %}</div></div></div></div>
</div>{% block js %}{% endblock %}</body>
</html>

2.首页后端

def home(request):return render(request, 'backend/home.html', locals())

三、后台功能之文章列表展示

1.文章列表展示前端

{% extends 'backend/home.html' %}{% block content %}<h2 class="text-center">文章列表</h2><form action=""><div class="form-group">文章标题<input type="text" id="title" class="form-control"></div><div class="form-group">文章分类<select name="" id="cate" class="form-control">{% for cate in cate_list %}<option value="{{ cate.pk }}">{{ cate.name }}</option>{% endfor %}</select></div><div class="form-group"><p>文章标签</p>{% for tags in tags_list %}{{ tags.name }}&nbsp;<input type="checkbox" value="{{ tags.pk }}" name="tags">&nbsp;&nbsp;{% endfor %}</div><div class="form-group">文章内容<textarea id="editor_id" name="content" style="width:100%;height:300px;"></textarea></div><div class="form-group"><input type="button" id="title" value="提交" class="btn btn-success btn-block btn_article"></div></form>{% endblock %}{% block js %}<script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script><script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script><script>KindEditor.ready(function (K) {window.editor = K.create('#editor_id', {height: '500px',items: ['source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste','plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright','justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript','superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/','formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold','italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage','flash', 'media', 'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak','anchor', 'link', 'unlink', '|', 'about'],colorTable: [['#E53333', '#E56600', '#FF9900', '#64451D', '#DFC5A4', '#FFE500'],['#009900', '#006600', '#99BB00', '#B8D100', '#60D978', '#00D5FF'],['#337FE5', '#003399', '#4C33E5', '#9933E5', '#CC33E5', '#EE33EE'],['#FFFFFF', '#CCCCCC', '#999999', '#666666', '#333333', '#000000']],resizeType: 0, // 0,1,2uploadJson : '/app02/upload_image/',extraFileUploadParams : {csrfmiddlewaretoken: '{{ csrf_token }}'},});});$(".btn_article").click(function () {// 接收参数// 标题let title = $("#title").val();//分类let cate_id = $("#cate").val();// 标签:由于是复选框,所以有可能有多个值的情况,需用循环取值// 定义一个数组,用于接收tags的值tags_arr = []var tags = $("input[name='tags']:checked");$.each(tags, function (index, value) {console.log(index, $(this).val());// 将复选框的值放入数组中tags_arr.push($(this).val());});console.log(tags_arr); // [3, 4]// 注意:由于这里的值是数组,在后端获取不到,需要转为字符串才能传给后端获取// 数组转字符串 joinvar tags_str = tags_arr.join(',')  // 3, 4console.log(tags_str);// 内容// 同步数据后可以直接取得textarea的valueeditor.sync();content = $('#editor_id').val(); // jQueryconsole.log(content)// 发起Ajax请求$.ajax({url: '',type: 'post',data: {title: title,cate_id: cate_id,tags: tags_str,content: content,csrfmiddlewaretoken: '{{ csrf_token }}',},success: function (res) {if (res.code === 200) {layer.msg(res.msg, {} ,function () {location.href = '/app02/article_list/'});} else {layer.msg(res.msg, {});}}});});</script>
{% endblock %}

2.文章列表后端

def article_list(request):# 查询出所有的文章列表articles_list = models.Article.objects.all()return render(request, 'backend/article_list.html', locals())

三、后台功能之添加文章

1.添加文章前端

{% extends 'backend/home.html' %}{% block content %}<h3 class="text-center">添加文章</h3><form action=""><div class="form-group">文章标题:<input type="text" id="title" class="form-control"></div><div class="form-group">文章分类:<select name="" id="cate" class="form-control">{% for category in category_list %}<option value="{{ category.pk }}">{{ category.name }}</option>{% endfor %}</select></div><div class="form-group"><p>文章标签:</p>{% for tags in tags_list %}{{ tags.name }}&nbsp;<input type="checkbox" id="title" name="tags" value="{{ tags.pk }}" style="margin-right: 10px;">{% endfor %}</div><div class="form-group">文章内容:<textarea id="editor_id" name="content" style="width:100%;height:400px;"></textarea></div><div class="form-group"><!-- type="submit" 会自动提交表单 --><!-- <button></button> 会自动提交表单,注意:当写在外面的时候才不会自动提交form表单 -->{# <input type="submit" value="提交" id="title" class="btn btn-success btn-block"> #}<!-- type="button" 不会自动提交表单 --><input type="button" value="提交" id="title" class="btn btn-success btn-block btn_article"></div></form>{% endblock %}

2.添加文章后端

def add_article(request):# 文本编辑器官网 http://www.kindsoft.net/down.phpuser_obj = models.UserInfo.objects.filter(pk=request.session.get('id')).first()if not user_obj:return redirect('/login/')blog = user_obj.blog# 查询所有的分类category_list = models.Category.objects.all()# 查询所有的标签列表tags_list = models.Tag.objects.all()back_dict = {'code': 200, 'msg': '添加成功', 'data': []}# 1.获取前段传递过来的数据if request.method == 'POST':title = request.POST.get('title')cate_id = request.POST.get('cate_id')content = request.POST.get('content')tags = request.POST.get('tags')  # 1, 2# tags原本是一个列表,但是前端传值时进行了转换,有列表转为了字符串# 在此,要先转位列表去使用tags_list = tags.split(',')  # [1, 2]# 2.参数验证if not title:back_dict['code'] = 1500back_dict['msg'] = '标题必须有'return JsonResponse(back_dict)if not cate_id:back_dict['code'] = 1501back_dict['msg'] = '分类必须选择'return JsonResponse(back_dict)if not tags:back_dict['code'] = 1502back_dict['msg'] = '标签必须选择'return JsonResponse(back_dict)if not content:back_dict['code'] = 1503back_dict['msg'] = '内容不能为空'return JsonResponse(back_dict)'''1.摘要截取的问题2.xss攻击的问题----->原理:有了script标签------>把提交过来的内容过滤出script标签,然后做删除解决方法:1.使用正则匹配 script,匹配到之后,做删除,这个方法很麻烦,不推荐2.利用第三方模块来处理:bs4模块pip install bs4BeautifulSoup它是用在爬虫里面,它能够筛选数据,清晰html数据BeautifulSoup('', 'html.parser')使用lxml的话需要安装 pip install lxmlBeautifulSoup('', 'lxml')'''# 解决xss攻击的问题from bs4 import BeautifulSoupsoup = BeautifulSoup(content, 'html.parser')# print(soup.find_all('a'))  # 写的a那么久就筛选a# print(soup.find_all('script'))# print(soup.text)  # 打印文本内容for tag in soup.find_all():# print(tag.name)if tag.name == 'script':# 删除scripttag.decompose()# 文章摘要直接从内容中截取 100字desc = soup.text[:100]# 3.数据入库,需要操作文章表,文章、标签的第三张表article_obj = models.Article.objects.create(title=title, content=str(soup), desc=desc, category_id=cate_id,blog=blog)# 操作标签的第三张表,数据是多对多,选择批量插入数据article_tag_list = []for i in tags_list:article_tag_obj = models.Article2Tag(article_id=article_obj.pk, tag_id=i)article_tag_list.append(article_tag_obj)models.Article2Tag.objects.bulk_create(article_tag_list)return JsonResponse(back_dict)return render(request, 'backend/add_article.html', locals())

四、后台功能之上传文件

1.添加文章前端

{% extends 'backend/home.html' %}{% block content %}<h3 class="text-center">添加文章</h3><form action=""><div class="form-group">文章标题:<input type="text" id="title" class="form-control"></div><div class="form-group">文章分类:<select name="" id="cate" class="form-control">{% for category in category_list %}<option value="{{ category.pk }}">{{ category.name }}</option>{% endfor %}</select></div><div class="form-group"><p>文章标签:</p>{% for tags in tags_list %}{{ tags.name }}&nbsp;<input type="checkbox" id="title" name="tags" value="{{ tags.pk }}" style="margin-right: 10px;">{% endfor %}</div><div class="form-group">文章内容:<textarea id="editor_id" name="content" style="width:100%;height:400px;"></textarea></div><div class="form-group"><!-- type="submit" 会自动提交表单 --><!-- <button></button> 会自动提交表单,注意:当写在外面的时候才不会自动提交form表单 -->{# <input type="submit" value="提交" id="title" class="btn btn-success btn-block"> #}<!-- type="button" 不会自动提交表单 --><input type="button" value="提交" id="title" class="btn btn-success btn-block btn_article"></div></form>{% endblock %}{% block js %}{% load static %}<script charset="utf-8" src="{% static 'kindeditor/kindeditor-all.js' %}"></script><script charset="utf-8" src="{% static 'kindeditor/lang/zh-CN.js' %}"></script><script>KindEditor.ready(function (K) {window.editor = K.create('#editor_id', {height: '300px',items: ['source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste','plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright','justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript','superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/','formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold','italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage','flash', 'media', 'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak','anchor', 'link', 'unlink', '|', 'about'],resizeType: 0,colorTable: [['#E53333', '#E56600', '#FF9900', '#64451D', '#DFC5A4', '#FFE500'],['#009900', '#006600', '#99BB00', '#B8D100', '#60D978', '#00D5FF'],['#337FE5', '#003399', '#4C33E5', '#9933E5', '#CC33E5', '#EE33EE'],['#FFFFFF', '#CCCCCC', '#999999', '#666666', '#333333', '#000000'],],uploadJson: '/upload_image/',extraFileUploadParams: {csrfmiddlewaretoken: '{{ csrf_token }}'},});});// 绑定点击事件$(".btn_article").click(function () {// 同步数据后可以直接取得textarea的valueeditor.sync();// 获取数据// 标题let title = $("#title").val();// 分类let cate = $("#cate").val();// 文章标签是复选框,所以通过属性选择器获取值,// 由于是复选框的缘故,可能会有多个值的情况出现,所以应该循环取值// 注意:获取复选框时,不要加上 .val(),循环取值的再加上let tags = $("input[name='tags']:checkbox");let tags_arr = []$.each(tags, function (index, value) {tags_arr.push($(this).val());});console.log(tags_arr); // [1,2,3,4]// 由于是数组,所以传递到后台的数据不是字符串,要先转为字符串// 在下面的Ajax的 data 中传值也要传递字符串var tags_str = tags_arr.join(',');console.log(tags_str);// 内容{#html = document.getElementById('editor_id').value; // 原生API#}{#html = K('#editor_id').val(); // KindEditor Node API#}let content = $("#editor_id").val(); // jQueryconsole.log(content);// 发起Ajax请求$.ajax({url: '',type: 'post',data: {title: title,cate: cate,tags: tags_str,content: content,csrfmiddlewaretoken: '{{ csrf_token }}'},success: function (res) {if (code === 200) {layer.msg(res.msg, {}, function () {location.href = '/app02/article_list/';});} else {layer.msg(res.msg, {});}}});});</script>
{% endblock %}

2.添加文章后端

# 上传图片
def upload_image(request):'''返回格式:josn//成功时{"error" : 0,"url" : "http://www.example.com/path/to/file.ext"}//失败时{"error" : 1,"message" : "错误信息"}'''# 方式1:if request.method == 'POST':file_obj = request.FILES.get('imgFile')# print(file_obj)  # <MultiValueDict: {'imgFile': [<InMemoryUploadedFile: 7.jpg (image/jpeg)>]}># 拼接上传文件的路径import osfrom django.conf import settings# /media/article_img/xx.123.pngBASE_DIE = os.path.join(settings.BASE_DIR, 'media', 'article_img')file_name = os.path.join(BASE_DIE, file_obj.name)with open(file_name, 'wb') as f:for line in file_obj:f.write(line)# return HttpResponse('OK')return JsonResponse({"error": 0,"url": "/media/article_img/%s" % file_obj.name})# 方式2:将文件名用uuid随机生成重命名# 生成新的文件名import uuidnew_str = str(uuid.uuid4())new_uuid = new_str.replace('-', '')new_file_name = new_uuid + '.' + file_obj.name.rsplit('.')[-1]new_file = os.path.join(BASE_DIE, new_file_name)with open(new_file, 'wb') as f:for line in file_obj:f.write(line)return JsonResponse({"error": 0,"url": "/media/article_img/%s" % new_file_name})

http://www.ppmy.cn/news/1052815.html

相关文章

[保研/考研机试] KY212 二叉树遍历 华中科技大学复试上机题 C++实现

题目链接&#xff1a; 二叉树遍历_牛客题霸_牛客网二叉树的前序、中序、后序遍历的定义&#xff1a; 前序遍历&#xff1a;对任一子树&#xff0c;先访问根&#xff0c;然后遍历其左子树&#xff0c;最。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/43719512169…

Android逆向进阶,APK签名问题

原理&#xff1a; APK签名的原理基于公钥加密和数字证书的机制。在APK签名过程中&#xff0c;开发者使用私钥对应用的数字摘要进行加密&#xff0c;生成签名文件。然后&#xff0c;开发者将应用和签名文件一同发布。当用户下载应用时&#xff0c;系统会使用开发者的公钥对签名…

国内常见的几款可视化Web组态软件

组态软件是一种用于控制和监控各种设备的软件&#xff0c;也是指在自动控制系统监控层一级的软件平台和开发环境。这类软件实际上也是一种通过灵活的组态方式&#xff0c;为用户提供快速构建工业自动控制系统监控功能的、通用层次的软件工具。通常用于工业控制&#xff0c;自动…

webrtc学习(七)-媒体协商

一.概述 媒体协商嘴主要的作用就是看通信双方都支持那些编解码器&#xff0c;这些编解码器又包含那些参数&#xff0c;比如音频的参数包括采样率&#xff0c;采样大小&#xff0c;通道数&#xff0c;对于视频的参数包括分辨率帧率等一系列参数&#xff0c;此外传输中用的payloa…

vue与vueComponent的关系

创建完组件之后 就会创建一个vueComponent构造函数 当注册成功这个组件并且在页面使用之后 就会创建一个vueComponent实例对象&#xff0c; 所以为了避免组件在使用过程中data对象中的值混乱 组件中的data要写成函数&#xff0c; 使得每次创建的组件实例对象都可以返回一…

electron+vue3全家桶+vite项目搭建【16.1】electron多窗口,pinia状态同步,扩展store方法,主动同步pinia的状态【推荐】

文章目录 引入实现效果如下实现步骤1.自定义pinia插件2.主进程补充同步处理 引入 demo项目地址 我们之前写了一个自动同步pinia状态的插件&#xff0c;可以参考如下文章 electronvue3全家桶vite项目搭建【16】electron多窗口&#xff0c;pinia状态无法同步更新问题解决 这里…

CAM实现的流程--基于Pytorch实现

CAM实现的流程 CAM类激活映射CAM是什么CAM与CNN CAM类激活映射 CAM是什么 可视化CNN的工具&#xff0c; CAM解释网络特征变化&#xff0c;CAM使得弱监督学习发展成为可能&#xff0c;可以慢慢减少对人工标注的依赖&#xff0c;能降低网络训练的成本。通过可视化&#xff0c;就…

sql developer 连不上oracle数据库 报错 ORA-01031: insufficient privileges

sql developer 连不上oracle数据库 报错 ORA-01031: insufficient privileges 1、问题描述2、问题原因3、解决方法4、sql developer 连接oracle 成功 1、问题描述 使用sys账户以SYSDBA角色登录失败 报错 ORA-01031: insufficient privileges 2、问题原因 因为没有给sys账户分…