tornado框架blog模块分析与使用


Posted in Python onNovember 21, 2013
#!/usr/bin/env python
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import markdown
import os.path
import re
import torndb
import tornado.auth
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import unicodedata
from tornado.options import define, options
#定义一些通用的配置信息,比如数据库的连接信息,端口信息
define("port", default=8888, help="run on the given port", type=int)
define("mysql_host", default="127.0.0.1:3306", help="blog database host")
define("mysql_database", default="blog", help="blog database name")
define("mysql_user", default="root", help="blog database user")
define("mysql_password", default="sa123", help="blog database password")
#定义Application信息,它是继承tornado.web.Application 的
class Application(tornado.web.Application):

 # __init__ 函数自动调用
    def __init__(self):

    #这里就是url对应的控制器,下面分别对应一个类,来处理里面的逻辑
        handlers = [
            (r"/", HomeHandler),
            (r"/archive", ArchiveHandler),
            (r"/feed", FeedHandler),
            (r"/entry/([^/]+)", EntryHandler),
            (r"/compose", ComposeHandler),
            (r"/auth/login", AuthLoginHandler),
            (r"/auth/logout", AuthLogoutHandler),
        ]

    #设置,如博客标题,模板目录,静态文件目录,xsrf,是否调试
        settings = dict(
            blog_title=u"Tornado Blog",
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            ui_modules={"Entry": EntryModule},
            xsrf_cookies=True,
            cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
            login_url="/auth/login",
            debug=True,
        )

     #然后调用tornado.web.Application类的__init__函数加载进来
        tornado.web.Application.__init__(self, handlers, **settings)
        # Have one global connection to the blog DB across all handlers

     #数据库连接信息
        self.db = torndb.Connection(
            host=options.mysql_host, database=options.mysql_database,
            user=options.mysql_user, password=options.mysql_password)
#基类,继承自tornado.web.RequestHandler 的,后面的类都是继承这个类的
class BaseHandler(tornado.web.RequestHandler):

#属性装饰器,使db函数变成一个属性,便于后面直接使用
    @property
    def db(self):
        return self.application.db

#获得当前的用户
    def get_current_user(self):
        user_id = self.get_secure_cookie("blogdemo_user")
        if not user_id: return None
        return self.db.get("SELECT * FROM authors WHERE id = %s", int(user_id))
#首页
class HomeHandler(BaseHandler):
    def get(self):


 #query 查询很多列
        entries = self.db.query("SELECT * FROM entries ORDER BY published "
                                "DESC LIMIT 5")
        if not entries:


     #redirect 重定向到一个url
            self.redirect("/compose")
            return


 #render 渲染一个模板,后面是参数
        self.render("home.html", entries=entries)

class EntryHandler(BaseHandler):
    def get(self, slug):


#get 得到一个值
        entry = self.db.get("SELECT * FROM entries WHERE slug = %s", slug)


#raise 触发一个错误信息,后面必须接类型
        if not entry: raise tornado.web.HTTPError(404)
        self.render("entry.html", entry=entry)

class ArchiveHandler(BaseHandler):
    def get(self):
        entries = self.db.query("SELECT * FROM entries ORDER BY published "
                                "DESC")
        self.render("archive.html", entries=entries)

class FeedHandler(BaseHandler):
    def get(self):
        entries = self.db.query("SELECT * FROM entries ORDER BY published "
                                "DESC LIMIT 10")
        self.set_header("Content-Type", "application/atom+xml")
        self.render("feed.xml", entries=entries)

class ComposeHandler(BaseHandler):
    #装饰器
    @tornado.web.authenticated
    def get(self):
        id = self.get_argument("id", None)
        entry = None
        if id:
            entry = self.db.get("SELECT * FROM entries WHERE id = %s", int(id))
        self.render("compose.html", entry=entry)
    @tornado.web.authenticated
    def post(self):
        id = self.get_argument("id", None)
        title = self.get_argument("title")
        text = self.get_argument("markdown")
        html = markdown.markdown(text)
        if id:
            entry = self.db.get("SELECT * FROM entries WHERE id = %s", int(id))
            if not entry: raise tornado.web.HTTPError(404)
            slug = entry.slug

        #execute是执行的意思
            self.db.execute(
                "UPDATE entries SET title = %s, markdown = %s, html = %s "
                "WHERE id = %s", title, text, html, int(id))
        else:
            slug = unicodedata.normalize("NFKD", title).encode(
                "ascii", "ignore")
            slug = re.sub(r"[^\w]+", " ", slug)
            slug = "-".join(slug.lower().strip().split())
            if not slug: slug = "entry"
            while True:
                e = self.db.get("SELECT * FROM entries WHERE slug = %s", slug)
                if not e: break
                slug += "-2"
            self.db.execute(
                "INSERT INTO entries (author_id,title,slug,markdown,html,"
                "published) VALUES (%s,%s,%s,%s,%s,UTC_TIMESTAMP())",
                self.current_user.id, title, slug, text, html)
        self.redirect("/entry/" + slug)

class AuthLoginHandler(BaseHandler, tornado.auth.GoogleMixin):
    @tornado.web.asynchronous
    def get(self):
        if self.get_argument("openid.mode", None):
            self.get_authenticated_user(self.async_callback(self._on_auth))
            return
        self.authenticate_redirect()

#这里定义一个函数,来供上面调用
    def _on_auth(self, user):
        if not user:
            raise tornado.web.HTTPError(500, "Google auth failed")
        author = self.db.get("SELECT * FROM authors WHERE email = %s",
                             user["email"])
        if not author:
            # Auto-create first author
            any_author = self.db.get("SELECT * FROM authors LIMIT 1")
            if not any_author:
                author_id = self.db.execute(
                    "INSERT INTO authors (email,name) VALUES (%s,%s)",
                    user["email"], user["name"])
            else:
                self.redirect("/")
                return
        else:
            author_id = author["id"]
        self.set_secure_cookie("blogdemo_user", str(author_id))
        self.redirect(self.get_argument("next", "/"))

class AuthLogoutHandler(BaseHandler):
    def get(self):
        self.clear_cookie("blogdemo_user")

    #get_argument为获得next参数的值,默认为"/"
        self.redirect(self.get_argument("next", "/"))

class EntryModule(tornado.web.UIModule):
    def render(self, entry):
        return self.render_string("modules/entry.html", entry=entry)
#入口函数
def main():
    tornado.options.parse_command_line()
   #创建一个服务器
    http_server = tornado.httpserver.HTTPServer(Application())
   #监听端口
    http_server.listen(options.port)

#启动服务
    tornado.ioloop.IOLoop.instance().start()
#调用的入口
if __name__ == "__main__":
    main()

最后总结一下:

1)tornado框架中提供的几个demo,都是以这种形式来创建一个应用的
2)对每一个控制器函数,要么是,只可能有2个对外的函数,一个是get,一个是post
3)数据库有3中调用方式,query,get,exec
4)获取参数的值使用 get_argument 函数
5)重定向用redirect 函数
6)所有的函数都是属性这个类的,所有都用self调用
7)渲染模板用render函数

Python 相关文章推荐
通过C++学习Python
Jan 20 Python
Python多线程编程(八):使用Event实现线程间通信
Apr 05 Python
python提取页面内url列表的方法
May 25 Python
PyChar学习教程之自定义文件与代码模板详解
Jul 17 Python
python编程实现12306的一个小爬虫实例
Dec 27 Python
Python JSON格式数据的提取和保存的实现
Mar 22 Python
Python GUI编程完整示例
Apr 04 Python
使用Python画股票的K线图的方法步骤
Jun 28 Python
python实现批量nii文件转换为png图像
Jul 18 Python
pycharm如何实现跨目录调用文件
Feb 28 Python
python如何快速生成时间戳
Jul 21 Python
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
May 28 Python
python迭代器的使用方法实例
Nov 21 #Python
python生成器的使用方法
Nov 21 #Python
python单链表实现代码实例
Nov 21 #Python
python双向链表实现实例代码
Nov 21 #Python
python二叉树遍历的实现方法
Nov 21 #Python
python二叉树的实现实例
Nov 21 #Python
python冒泡排序算法的实现代码
Nov 21 #Python
You might like
ThinkPHP3.0略缩图不能保存到子目录的解决方法
2012/09/30 PHP
深入解析fsockopen与pfsockopen的区别
2013/07/05 PHP
PHP基于自定义类随机生成姓名的方法示例
2017/08/05 PHP
PHP实现的AES双向加密解密功能示例【128位】
2018/09/03 PHP
JavaScript prototype对象的属性说明
2010/03/13 Javascript
js word表格动态添加代码
2010/06/07 Javascript
JavaScript中创建类/对象的几种方法总结
2013/11/29 Javascript
JavaScript中Number.NEGATIVE_INFINITY值的使用详解
2015/06/05 Javascript
js中 javascript:void(0) 用法详解
2015/08/11 Javascript
easyui-combobox 实现简单的自动补全功能示例
2016/11/08 Javascript
基于node.js之调试器详解
2017/08/22 Javascript
Angularjs自定义指令实现分页插件(DEMO)
2017/09/16 Javascript
浅谈angular2路由预加载策略
2017/10/04 Javascript
快速搭建vue2.0+boostrap项目的方法
2018/04/09 Javascript
vue项目设置scrollTop不起作用(总结)
2018/12/21 Javascript
vue 开发之路由配置方法详解
2019/12/02 Javascript
jquery实现弹窗(系统提示框)效果
2019/12/10 jQuery
react基本安装与测试示例
2020/04/27 Javascript
详解ES6新增字符串扩张方法includes()、startsWith()、endsWith()
2020/05/12 Javascript
Object.keys() 和 Object.getOwnPropertyNames() 的区别详解
2020/05/21 Javascript
微信小程序整个页面的自动适应布局的实现
2020/07/12 Javascript
ant design vue中表格指定格式渲染方式
2020/10/28 Javascript
[01:46]辉夜杯—打造中国DOTA新格局
2015/12/25 DOTA
[00:26]TI7不朽珍藏III——冥界亚龙不朽展示
2017/07/15 DOTA
Python使用函数默认值实现函数静态变量的方法
2014/08/18 Python
python轻松实现代码编码格式转换
2015/03/26 Python
python合并同类型excel表格的方法
2018/04/01 Python
浅谈PyQt5 的帮助文档查找方法,可以查看每个类的方法
2019/06/25 Python
python GUI库图形界面开发之PyQt5线程类QThread详细使用方法
2020/02/26 Python
Python实现JS解密并爬取某音漫客网站
2020/10/23 Python
英国外籍人士的在线超市:British Corner Shop
2019/06/03 全球购物
统计每一学生的平均成绩
2014/06/06 面试题
弘扬雷锋精神活动演讲稿
2014/03/04 职场文书
辩论赛主持词
2014/03/18 职场文书
化验员岗位职责
2015/02/14 职场文书
军训后的感想
2015/08/07 职场文书