分支:edit_blogs
url.py添加对应新增的功能:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 # coding:utf-8 3 4 import tornado.web 5 import application 6 7 url = [(r"^/(favicon\.ico)", tornado.web.StaticFileHandler, 8 dict(path=application.settings['static_path']))] 9 10 url += [(r"^/", "handlers.index.IndexHandler")] 11 12 url += [(r'^/help', "handlers.help.HelpHandler")] 13 14 url += [(r'^/register', "handlers.auth.RegisterHandler")] 15 url += [(r'^/login', "handlers.auth.LoginHandler")] 16 url += [(r'^/logout', "handlers.auth.LogoutHandler")] 17 url += [(r'^/changepasswd', "handlers.auth.ChangePasswdHandler")] 18 url += [(r'^/account/(\w+)', "handlers.auth.AccountHandler")] 19 20 url += [(r'^/manage/blogs/(\w+)', "handlers.manage.BlogsHandler")] 21 url += [(r'^/manage/comments/(\w+)', "handlers.manage.CommentsHandler")] 22 23 url += [(r'^/blog/new/(\w+)', "handlers.blog.NewBlogHandler")] 24 url += [(r'^/blog/edit/(\w+)', "handlers.blog.EditBlogHandler")]
handles文件夹添加2个对应的处理
1、blog.py(处理文件的新增和编辑)
2、manage.py (处理日志列表的查看)
blog.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 4 import tornado.web 5 import logging 6 import datetime 7 import base64 8 9 from base import BaseHandler 10 11 12 class NewBlogHandler(BaseHandler): 13 14 ''' 15 后台管理(新建博客) 16 ''' 17 18 @tornado.web.authenticated 19 def get(self, *args, **kwargs): 20 action = "_%s_action" % args[0] 21 if hasattr(self, action): 22 getattr(self, action)() 23 else: 24 self.render("index.htm", 25 user=self.current_user, 26 admin=self.get_current_permission()) 27 28 def _info_action(self): 29 self.render("blog/newblog.htm", 30 user=self.current_user, 31 admin=self.get_current_permission()) 32 33 @tornado.web.authenticated 34 def post(self, *args, **kwargs): 35 action = "_%s_action" % args[0] 36 if hasattr(self, action): 37 getattr(self, action)() 38 else: 39 self.json("fail", "no action!") 40 41 def _add_blog_action(self): 42 ''' 43 添加新博客 44 ''' 45 blog_title = self.get_body_argument("blog_title", default="") 46 blog_summary = self.get_body_argument("blog_summary", default="") 47 blog_content = self.get_body_argument("blog_content", default="") 48 49 try: 50 self.db.execute('''insert into blogs 51 ( 52 user_id, 53 user_name, 54 user_image, 55 name, 56 summary, 57 content, 58 created_at 59 ) 60 values 61 ( 62 (select id from users where name = %s), 63 %s, 'none', %s, %s, %s, %s 64 )''', 65 self.current_user, 66 self.current_user, 67 blog_title, 68 blog_summary, 69 blog_content, 70 datetime.datetime.now()) 71 self.json("success", blog_title) 72 except Exception as e: 73 self.json("error", str(e)) 74 75 class EditBlogHandler(BaseHandler): 76 77 ''' 78 后台管理(编辑博客) 79 ''' 80 81 @tornado.web.authenticated 82 def get(self, *args, **kwargs): 83 action = "_%s_action" % args[0] 84 if hasattr(self, action): 85 getattr(self, action)(*args, **kwargs) 86 else: 87 self.render("index.htm", 88 user=self.current_user, 89 admin=self.get_current_permission()) 90 91 def _info_action(self, *args, **kwargs): 92 blog_id = self.get_argument("id", default="") 93 self.render("blog/editblog.htm", 94 user=self.current_user, 95 admin=self.get_current_permission(), 96 blog_id=blog_id) 97 98 @tornado.web.authenticated 99 def post(self, *args, **kwargs): 100 action = "_%s_action" % args[0] 101 if hasattr(self, action): 102 getattr(self, action)() 103 else: 104 self.json("fail", "no action!") 105 106 def _edit_blog_info_action(self): 107 ''' 108 查看编辑的博客的信息 109 ''' 110 blog_id = self.get_body_argument("blog_id", default="") 111 112 ret = self.db.query('''select 113 id, 114 name as blog_title, 115 summary as blog_summary, 116 content as blog_content 117 from 118 blogs 119 where 120 id = %s''', blog_id) 121 self.json("success", ret) 122 123 def _update_blog_action(self): 124 ''' 125 更新博客 126 ''' 127 blog_id = self.get_body_argument("blog_id", default="") 128 blog_title = self.get_body_argument("blog_title", default="") 129 blog_summary = self.get_body_argument("blog_summary", default="") 130 blog_content = self.get_body_argument("blog_content", default="") 131 132 try: 133 self.db.execute('''update 134 blogs 135 set 136 name = %s, 137 summary = %s, 138 content = %s 139 where 140 id = %s''', 141 blog_title, 142 blog_summary, 143 blog_content, 144 blog_id) 145 self.json("success", blog_id) 146 except Exception as e: 147 self.json("error", str(e)) 148
manage.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 4 import tornado.web 5 import logging 6 import datetime 7 import base64 8 9 from base import BaseHandler 10 11 12 class BlogsHandler(BaseHandler): 13 14 ''' 15 后台管理(管理博客) 16 ''' 17 18 @tornado.web.authenticated 19 def get(self, *args, **kwargs): 20 action = "_%s_action" % args[0] 21 if hasattr(self, action): 22 getattr(self, action)() 23 else: 24 self.render("index.htm", 25 user=self.current_user, 26 admin=self.get_current_permission()) 27 28 def _info_action(self): 29 self.render("manage/blogs.htm", 30 user=self.current_user, 31 admin=self.get_current_permission()) 32 33 @tornado.web.authenticated 34 def post(self, *args, **kwargs): 35 action = "_%s_action" % args[0] 36 if hasattr(self, action): 37 getattr(self, action)() 38 else: 39 self.json("fail", "no action!") 40 41 def _query_all_blogs_action(self): 42 ''' 43 查询所有博客简略信息 44 ''' 45 page_record = 10 46 current_page = self.get_body_argument("current_page", default="1") 47 page_dict = dict() 48 49 try: 50 ret = self.db.query('''select count(id) as count from blogs''') 51 if ret[0].count%page_record == 0: 52 total_pages = ret[0].count//page_record 53 else: 54 total_pages = ret[0].count//page_record + 1 55 page_dict["total_pages"] = str(total_pages) 56 page_dict["total_count"] = str(ret[0].count) 57 ret = self.db.query('''select 58 id, 59 user_name, 60 user_image, 61 name as blog_title, 62 summary as blog_summary, 63 DATE_FORMAT(created_at, %s) as created_at 64 from 65 blogs 66 order by id desc limit %s, %s''', 67 '%Y-%m-%d %H:%i:%s', 68 (int(current_page)-1)*page_record, page_record) 69 page_dict["current_page"] = current_page 70 page_dict["current_data"] = ret 71 self.json("success", page_dict) 72 except Exception as e: 73 self.json("error", str(e)) 74 75 76 def _delete_blog_action(self): 77 ''' 78 删除博客 79 ''' 80 blog_id = self.get_body_argument("blog_id", default="") 81 try: 82 self.db.execute('''delete from blogs where id = %s''', blog_id) 83 self.json("success", blog_id) 84 except Exception as e: 85 self.json("error", str(e)) 86 87 class CommentsHandler(BaseHandler): 88 89 ''' 90 后台管理(管理评论) 91 ''' 92 93 @tornado.web.authenticated 94 def get(self, *args, **kwargs): 95 action = "_%s_action" % args[0] 96 if hasattr(self, action): 97 getattr(self, action)() 98 else: 99 self.render("index.htm", 100 user=self.current_user, 101 admin=self.get_current_permission()) 102 103 def _info_action(self): 104 self.render("manage/comments.htm", 105 user=self.current_user, 106 admin=self.get_current_permission()) 107 108 @tornado.web.authenticated 109 def post(self, *args, **kwargs): 110 action = "_%s_action" % args[0] 111 if hasattr(self, action): 112 getattr(self, action)() 113 else: 114 self.json("fail", "no action!")
templates文件夹,添加对应的html文件
1、blogs.htm 显示日志列表,用表格来展示
2、blog/newblog.htm 新建日志
3、blog/editblog.htm 编辑日志
blogs.htm
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 {% extends "../base.htm" %} 2 3 {%block header%} 4 {%include '../header.htm'%} 5 {%end%} 6 7 {%block content%} 8 <script src="{{ static_url('js/knockout/knockout-2.1.0.js') }}"></script> 9 <script src="{{ static_url('js/knockout/knockout.mapping-2.4.1.js') }}"></script> 10 <script src="{{ static_url('js/components/jquery.twbsPagination.min.js') }}"></script> 11 <script src="{{ static_url('js/manage/blogs.js') }}"></script> 12 13 <div class="bs-docs-container"> 14 <div class="container"> 15 <div class="row"> 16 <div class="col-md-12"> 17 <div class="row" style="margin-top: 10px; margin-bottom: 10px"> 18 <div class="col-lg-6"> 19 <div class="input-group"> 20 <span class="input-group-btn"> 21 <a href="/blog/new/info" role="button" class="btn btn-info" data-toggle="modal"><span class="glyphicon glyphicon-plus"> 新日志</span></a> 22 </span> 23 </div> 24 </div> 25 </div> 26 27 <div style="min-height: 550px"> 28 <table class="table table-hover table-bordered"> 29 <thead> 30 <tr> 31 <th colspan="8"> 32 <h3 class="table_title" id="table_title">日 志 列 表</h3> 33 </th> 34 </tr> 35 36 <tr class="table_header"> 37 <td>序号</td> 38 {% if admin is 1%} 39 <td>ID</td> 40 {% end %} 41 <td>标题</td> 42 <td>作者</td> 43 <td>简介</td> 44 <td>日期</td> 45 <td></td> 46 </tr> 47 </thead> 48 <tbody> 49 <!-- ko foreach: $data --> 50 <tr> 51 <td data-bind="text: $index() + 1"></td> 52 {% if admin is 1%} 53 <td > 54 <span id="blog_id" data-bind="text: id" /> 55 </td> 56 {% end %} 57 <td > 58 <button class="btn btn-link" id="blogTitleBtn" data-bind="text: show_title(blog_title), click:function(name){show_blog(name)}"></span></button> 59 </td> 60 <td > 61 <button class="btn btn-link" id="blogAuthorBtn" data-bind="text: user_name, click:function(name){show_author(name)}"></span></button> 62 </td> 63 <td > 64 <span id="blog_summary" data-bind="text: show_summary(blog_summary)" /> 65 </td> 66 <td > 67 <span id="created_at" data-bind="text: created_at" /> 68 </td> 69 <td> 70 <button class="btn btn-link" id="deleteButton" data-bind="click:function(name){edit_blog(name)}"><span class="glyphicon glyphicon-edit">编辑</span></button> 71 72 <button class="btn btn-link" id="deleteButton" data-bind="click:function(name){show_blog(name)}"><span class="glyphicon glyphicon-search">查看</span></button> 73 74 <button class="btn btn-link" id="deleteButton" data-bind="click:function(name){delete_blog(name)}"><span class="glyphicon glyphicon-trash">删除</span></button> 75 </td> 76 </tr> 77 <!-- /ko --> 78 </tbody> 79 </table> 80 </div> 81 <div id="pagination_box" class="pull-right"> 82 <ul id="pagination_zc" class="pagination-sm"></ul> 83 </div> 84 </div> 85 </div> 86 </div> 87 </div> 88 {%end%} 89 90 {%block footer%} 91 {%include '../footer.htm'%} 92 {%end%}
对应的js/manage/blogs.js
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 初始化时加载 3 */ 4 // ko数据绑定 5 var model; 6 var viewModel; 7 8 // 分页 9 var page_size = 1 10 var total_pages = 1; 11 var visiblePages = 8; 12 13 $(document).ready(function () { 14 get_all_blogs(1); 15 }); 16 17 /** 18 * 分页 19 */ 20 function twbsPagination() { 21 $('#pagination_zc').twbsPagination({ 22 totalPages: total_pages, 23 visiblePages: visiblePages > 8 ? 8 : visiblePages, 24 startPage: 1, 25 first: "首页", 26 prev: "上一页", 27 next: "下一页", 28 last: "未页", 29 onPageClick: function (event, page) { 30 get_all_blogs(page); 31 } 32 }); 33 } 34 35 /** 36 * 获取所有日志信息 37 */ 38 function get_all_blogs(current_page) { 39 $.ajax({ 40 type: "POST", 41 url: "/manage/blogs/query_all_blogs", 42 data: { 43 "_xsrf": getCookie("_xsrf"), 44 "current_page": current_page 45 }, 46 success: function (data) { 47 if(data["status"] == "success"){ 48 total_pages = parseInt(data["info"].total_pages); 49 if(total_pages == 0) 50 { 51 total_pages = 1; 52 } 53 54 if(viewModel == undefined){ 55 viewModel = ko.mapping.fromJS(data["info"].current_data); 56 model = ko.toJS(viewModel); 57 ko.applyBindings(viewModel); 58 twbsPagination(); 59 } 60 else{ 61 if(page_size != total_pages){ 62 $('#pagination_zc').remove(); 63 $("#pagination_box").append('<ul id="pagination_zc" class="pagination-sm"></ul>'); 64 twbsPagination(); 65 } 66 ko.mapping.fromJS(data["info"].current_data, viewModel); 67 model = ko.toJS(viewModel); 68 } 69 $("#table_title").html("日 志 列 表 (" + data["info"].total_count + ")"); 70 page_size = total_pages; 71 } 72 }, 73 error : function() { 74 errNotify("可能为网络异常,请检查您的网络!"); 75 } 76 }); 77 } 78 79 /** 80 * 显示博客信息 81 */ 82 function show_blog(name) { 83 console.log("待完成"); 84 console.log(name.id()); 85 console.log(name.blog_title()); 86 } 87 88 /** 89 * 编辑博客 90 */ 91 function edit_blog(name) { 92 window.location.href = "/blog/edit/info?id=" + name.id(); 93 } 94 95 /** 96 * 删除博客 97 */ 98 function delete_blog(name) { 99 $.messager.model = { 100 ok:{ text: "残忍删除", classed: 'btn-info' }, 101 cancel: { text: "勉强挽留", classed: 'btn-default' } 102 }; 103 104 $.messager.confirm("删除日志", "确定删除此日志吗?", function() { 105 var blog_id = name.id(); 106 $.ajax({ 107 type: "POST", 108 url: "/manage/blogs/delete_blog", 109 data: { 110 "_xsrf": getCookie("_xsrf"), 111 "blog_id": blog_id 112 }, 113 success: function (data) { 114 if(data["status"] == "error"){ 115 errNotify("删除错误!\r\n 请检查网络状态是否正常!"); 116 } 117 else{ 118 viewModel.remove(name); 119 model = ko.toJS(viewModel); 120 successNotify("删除成功!"); 121 } 122 }, 123 error : function() { 124 errNotify("error!"); 125 } 126 }); 127 }); 128 } 129 130 /** 131 * 显示作者信息 132 */ 133 function show_author(name) { 134 console.log("待完成"); 135 } 136 137 /** 138 * 显示日志标题信息 139 */ 140 function show_title(blog_title) { 141 var title = blog_title() 142 if(title.length > 25){ 143 return title.slice(0, 25) + "..."; 144 } 145 return title 146 } 147 148 /** 149 * 显示日志简介信息 150 */ 151 function show_summary(blog_summary) { 152 var summary = blog_summary() 153 if(summary.length > 25){ 154 return summary.slice(0, 25) + "..."; 155 } 156 return summary 157 }
newblog.htm
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 {% extends "../base.htm" %} 2 3 {%block header%} 4 {%include '../header.htm'%} 5 {%end%} 6 7 {%block content%} 8 <link rel="stylesheet" href="{{ static_url('css/components/bootstrap-markdown.min.css') }}"> 9 <script src="{{ static_url('js/components/bootstrap-markdown.js') }}"></script> 10 <script src="{{ static_url('js/components/to-markdown.js') }}"></script> 11 <script src="{{ static_url('js/components/markdown.js') }}"></script> 12 <script src="{{ static_url('js/blog/newblog.js') }}"></script> 13 14 <div class="bs-docs-container"> 15 <div class="container"> 16 <div class="row"> 17 <div class="col-md-12"> 18 <div class="row" style="margin-top: 10px; margin-bottom: 10px"> 19 <input type="text" id="blog_title" class="form-control" placeholder="日志标题" required> 20 21 <textarea id="blog_summary" style="width:100%" placeholder="简要说明"></textarea> 22 23 <textarea id="blog_content" data-provide="markdown" placeholder="日志正文" rows="25"></textarea> 24 <hr/> 25 <div role="button" class="btn btn-info" onclick="add_blog()" style="float: right"><span class="glyphicon glyphicon-save"> 保 存</span></div> 26 </div> 27 </div> 28 </div> 29 </div> 30 </div> 31 {%end%} 32 33 {%block footer%} 34 {%include '../footer.htm'%} 35 {%end%}
对应的js/blog/newblog.js
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 初始化时加载 3 */ 4 $(document).ready(function () { 5 6 }); 7 8 /** 9 * 新增日志 10 */ 11 function add_blog() { 12 var blog_title = $("#blog_title").val(); 13 var blog_summary = $("#blog_summary").val(); 14 var blog_content = $("#blog_content").val(); 15 16 if(blog_title == "") 17 { 18 errNotify("标题不能为空!"); 19 return; 20 } 21 else if(blog_content == "") 22 { 23 errNotify("正文不能为空!"); 24 return; 25 } 26 27 $.ajax({ 28 type: "POST", 29 url: "/blog/new/add_blog", 30 data: { 31 "_xsrf": getCookie("_xsrf"), 32 "blog_title": blog_title, 33 "blog_summary": blog_summary, 34 "blog_content": blog_content 35 }, 36 success: function (data) { 37 if(data["status"] == "fail"){ 38 warningNotify("新增失败!" + data["info"]); 39 } 40 else{ 41 successNotify("新增成功!"); 42 } 43 }, 44 error : function() { 45 errNotify("可能为网络异常,请检查您的网络!"); 46 } 47 }); 48 }
editblog.htm
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 {% extends "../base.htm" %} 2 3 {%block header%} 4 {%include '../header.htm'%} 5 {%end%} 6 7 {%block content%} 8 <link rel="stylesheet" href="{{ static_url('css/components/bootstrap-markdown.min.css') }}"> 9 <script src="{{ static_url('js/components/bootstrap-markdown.js') }}"></script> 10 <script src="{{ static_url('js/components/to-markdown.js') }}"></script> 11 <script src="{{ static_url('js/components/markdown.js') }}"></script> 12 <script src="{{ static_url('js/blog/editblog.js') }}"></script> 13 14 <div class="bs-docs-container"> 15 <div class="container"> 16 <div class="row"> 17 <div class="col-md-12"> 18 <div class="row" style="margin-top: 10px; margin-bottom: 10px"> 19 <h4 id="blog_id" style="display: none">{{blog_id}}</h4> 20 21 <input type="text" style="font-weight:bold;" id="blog_title" class="form-control" placeholder="日志标题" required> 22 23 <textarea id="blog_summary" style="width:100%" placeholder="简要说明"></textarea> 24 25 <textarea id="blog_content" data-provide="markdown" placeholder="日志正文" rows="25" autofocus></textarea> 26 <hr/> 27 <div role="button" class="btn btn-info" onclick="update_blog()" style="float: right"><span class="glyphicon glyphicon-save"> 更 新</span></div> 28 </div> 29 </div> 30 </div> 31 </div> 32 </div> 33 {%end%} 34 35 {%block footer%} 36 {%include '../footer.htm'%} 37 {%end%}
对应的js/blog/editblog.js
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 初始化时加载 3 */ 4 $(document).ready(function () { 5 show_edit_blog(); 6 }); 7 8 function show_edit_blog() { 9 blog_id = $("#blog_id").text(); 10 11 if (blog_id == "") { 12 errNotify("缺少必要的参数(blog_id)!"); 13 return; 14 } 15 16 $.ajax({ 17 type: "POST", 18 url: "/blog/edit/edit_blog_info", 19 data: { 20 "_xsrf": getCookie("_xsrf"), 21 "blog_id": blog_id 22 }, 23 success: function (data) { 24 if(data["status"] == "fail"){ 25 warningNotify("查询失败!可能此日志已被删除!" + data["info"]); 26 } 27 else{ 28 $("#blog_title").val(data["info"][0].blog_title); 29 $("#blog_summary").val(data["info"][0].blog_summary); 30 $("#blog_content").val(data["info"][0].blog_content); 31 } 32 }, 33 error : function() { 34 errNotify("可能为网络异常,请检查您的网络!"); 35 } 36 }); 37 } 38 39 /** 40 * 更新日志 41 */ 42 function update_blog() { 43 var blog_id = $("#blog_id").text(); 44 var blog_title = $("#blog_title").val(); 45 var blog_summary = $("#blog_summary").val(); 46 var blog_content = $("#blog_content").val(); 47 48 if(blog_title == "") 49 { 50 errNotify("标题不能为空!"); 51 return; 52 } 53 else if(blog_content == "") 54 { 55 errNotify("正文不能为空!"); 56 return; 57 } 58 59 $.ajax({ 60 type: "POST", 61 url: "/blog/edit/update_blog", 62 data: { 63 "_xsrf": getCookie("_xsrf"), 64 "blog_id": blog_id, 65 "blog_title": blog_title, 66 "blog_summary": blog_summary, 67 "blog_content": blog_content 68 }, 69 success: function (data) { 70 if(data["status"] == "success"){ 71 successNotify("更新成功!"); 72 } 73 else{ 74 warningNotify("更新失败!" + data["info"]); 75 } 76 }, 77 error : function() { 78 errNotify("可能为网络异常,请检查您的网络!"); 79 } 80 }); 81 }
日志列表图:
日志新建/编辑