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 相关文章推荐
python控制台显示时钟的示例
Feb 24 Python
python装饰器初探(推荐)
Jul 21 Python
Python数据分析matplotlib设置多个子图的间距方法
Aug 03 Python
pycharm 取消默认的右击运行unittest的方法
Nov 29 Python
Python 利用pydub库操作音频文件的方法
Jan 09 Python
Python中常用的内置方法
Jan 28 Python
利用anaconda保证64位和32位的python共存
Mar 09 Python
AUC计算方法与Python实现代码
Feb 28 Python
浅谈Python 钉钉报警必备知识系统讲解
Aug 17 Python
python中turtle库的简单使用教程
Nov 11 Python
详解Python中list[::-1]的几种用法
Nov 16 Python
利用python实现后端写网页(flask框架)
Feb 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
php 引用(&)详解
2009/11/20 PHP
php笔记之:数据类型与常量的使用分析
2013/05/14 PHP
如何使用php输出时间格式
2013/08/31 PHP
PHP给文字内容中的关键字进行套红处理
2016/04/12 PHP
基于jquery的lazy loader插件实现图片的延迟加载[简单使用]
2011/05/07 Javascript
js随机颜色代码的多种实现方式
2013/04/23 Javascript
基于SVG的web页面图形绘制API介绍及编程演示
2013/06/28 Javascript
Javascript编程中几种继承方式比较分析
2015/11/28 Javascript
jquery动态遍历Json对象的属性和值的方法
2016/07/27 Javascript
angular route中使用resolve在uglify压缩后问题解决
2016/09/21 Javascript
NodeJS整合银联网关支付(DEMO)
2016/11/09 NodeJs
js实现登录注册框手机号和验证码校验(前端部分)
2017/09/28 Javascript
使用express+multer实现node中的图片上传功能
2018/02/02 Javascript
详解vue 数据传递的方法
2018/04/19 Javascript
vue 解决addRoutes动态添加路由后刷新失效问题
2018/07/02 Javascript
ECharts地图绘制和钻取简易接口详解
2019/07/12 Javascript
Python的内存泄漏及gc模块的使用分析
2014/07/16 Python
解决pycharm 工具栏Tool中找不到Run manager.py Task的问题
2019/07/01 Python
Python列表(list)所有元素的同一操作解析
2019/08/01 Python
python实现KNN分类算法
2019/10/16 Python
Python中os模块功能与用法详解
2020/02/26 Python
Python drop方法删除列之inplace参数实例
2020/06/27 Python
Python字节单位转换(将字节转换为K M G T)
2021/03/02 Python
HTML5 placeholder属性详解
2016/06/22 HTML / CSS
基于HTML5代码实现折叠菜单附源码下载
2015/11/27 HTML / CSS
Eyeko美国:屡获殊荣的睫毛膏、眼线笔和眉妆
2018/07/05 全球购物
Mio Skincare英国官网:身体紧致及孕期身体护理
2018/08/19 全球购物
杭州-飞时达软件有限公司.net笔面试
2012/04/28 面试题
C#基础面试题
2016/10/17 面试题
《阳光》教学反思
2014/02/23 职场文书
质量负责人任命书
2014/06/06 职场文书
远程培训的心得体会
2014/09/01 职场文书
水电工程师岗位职责
2015/02/13 职场文书
博士论文答辩开场白
2015/06/01 职场文书
教师理论学习心得体会
2016/01/21 职场文书
解决thinkphp6(tp6)在状态码500下不报错,或者显示错误“Malformed UTF-8 characters”的问题
2021/04/01 PHP