一、后台功能的初始配置
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" > < 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> < 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; " > {# < 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> </ div>
</ 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 , uploadJson : '/app02/upload_image/' , extraFileUploadParams : { csrfmiddlewaretoken : '{{ csrf_token }}' } , } ) ; } ) ; $ ( ".btn_article" ) . click ( function ( ) { let title = $ ( "#title" ) . val ( ) ; let cate_id = $ ( "#cate" ) . val ( ) ; 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) ; var tags_str = tags_arr. join ( ',' ) console. log ( tags_str) ; editor. sync ( ) ; content = $ ( '#editor_id' ) . val ( ) ; console. log ( content) $. 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) : user_obj = models. UserInfo. objects. filter ( pk= request. session. get( 'id' ) ) . first( ) if not user_obj: return redirect( '/login/' ) blog = user_obj. blogcategory_list = models. Category. objects. all ( ) tags_list = models. Tag. objects. all ( ) back_dict = { 'code' : 200 , 'msg' : '添加成功' , 'data' : [ ] } 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' ) tags_list = tags. split( ',' ) if not title: back_dict[ 'code' ] = 1500 back_dict[ 'msg' ] = '标题必须有' return JsonResponse( back_dict) if not cate_id: back_dict[ 'code' ] = 1501 back_dict[ 'msg' ] = '分类必须选择' return JsonResponse( back_dict) if not tags: back_dict[ 'code' ] = 1502 back_dict[ 'msg' ] = '标签必须选择' return JsonResponse( back_dict) if not content: back_dict[ 'code' ] = 1503 back_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')''' from bs4 import BeautifulSoupsoup = BeautifulSoup( content, 'html.parser' ) for tag in soup. find_all( ) : if tag. name == 'script' : tag. decompose( ) desc = soup. text[ : 100 ] 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 ( ) { editor. sync ( ) ; let title = $ ( "#title" ) . val ( ) ; let cate = $ ( "#cate" ) . val ( ) ; let tags = $ ( "input[name='tags']:checkbox" ) ; let tags_arr = [ ] $. each ( tags, function ( index, value ) { tags_arr. push ( $ ( this ) . val ( ) ) ; } ) ; console. log ( tags_arr) ; var tags_str = tags_arr. join ( ',' ) ; console. log ( tags_str) ; { #html = document. getElementById ( 'editor_id' ) . value; { #html = K ( '#editor_id' ) . val ( ) ; let content = $ ( "#editor_id" ) . val ( ) ; console. log ( content) ; $. 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" : "错误信息"}''' if request. method == 'POST' : file_obj = request. FILES. get( 'imgFile' ) import osfrom django. conf import settingsBASE_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 JsonResponse( { "error" : 0 , "url" : "/media/article_img/%s" % file_obj. name} ) 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} )