文章目录
  1. 1. 简介
  2. 2. 关于安装
  3. 3. 项目构建
  4. 4. 视图函数和路由
    1. 4.1. 动态路由:
  5. 5. 前端模板
    1. 5.0.1. SimpleTemplate的语法
  • 6. 数据库
  • 7. API编写与参考
  • 8. 项目部署
  • 9. 其他特性(待加入)
    1. 9.1. Session
    2. 9.2. 一些推荐的可用的插件
  • 10. Bottle学习资源
  • 简介

    “Bottle是一个快速,简单,轻量级的Python WSGI Web框架。它小巧但高效,整个框架只有一个文件,却自带了路径映射(route)、模板(template)、简单的数据库访问(post,get等)等web框架组件。它只依赖Python标准库。”

    • URL映射(Routing):将URL请求映射到Python函数。
    • 模板(Templates):内置了一个简单快速的模板引擎,并支持其他模板引擎,如Mako,Jinja2,cheetah等
    • 实用工具(Utilities):内置了很多工具来提供表单数据的访问,文件上传,Cookies处理,HTTP头信息处理和访问其他HTTP相关信息的功能.)
    • 服务器(Server):Bottle内置了一个用于开发环境的Web服务器,在生产环境下还支持paste,gae,Google App Engine,cherrypy等符合WSGI标准的HTTP服务器。

    成熟的web框架应该有:基本HTTP请求处理、GET或POST数据的接受、模板、数据库、session等功能。

    可是bottle并没有提供配置文件集成,数据库管理,可扩展的中间件等特性,所以它并不是开发复杂项目的第一选择。对于大型的Web程序,Bottle的功能略显不足,程序员需要手动管理模块、数据库、配置等等。

    而如果你只是想快速创建一个Restful API接口,或者只想用网络开发框架的做一个简单的应用,Bottle可以轻松地满足你的要求。它具备了你将需要的所有功能:路由、模板、访问请求与响应数据、支持多种网络服务器以及WebSockets等高级功能。

    关于安装

    前面说到它小巧,它的小巧从安装就能看出来。(在虚拟环境下)

    首先你可以选择安装它:

    1
    2
    $ pip install bottle==0.12.8
    $ pip freeze > requirements.txt

    你也可以不安装它,直接下载bottle.py文件复制到自己的应用中就可以使用了。

    $ wget http://bottlepy.org/bottle.py
    

    项目构建

    好像没有构建项目的必要,但如果你非要构建的话,这里有一个第三方开发的插件bottle-boilerplate可用来自动构建项目(这个插件star也不多..)。

    首先来安装它:

    $ pip install bottle-boilerplate
    

    然后就可以构建项目了:

    1
    2
    3
    $ bottle startproject YOUR_PROJECT_NAME
    $ cd YOUR_PROJECT_NAME
    $ pip install -r requirements.txt

    然后就可以看到这个项目结构是这样的(这里我创建了bottle文件夹来放我的项目,我的项目名为bottleapp):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    |- bottle
    |- bottleapp
    |- bottleapp
    |- controllers
    |- home.py
    |- __init__.py
    |- models
    |- __iniy__.py
    |- views
    |- index.html
    |- __init__.py
    |- routes.py
    |- settings.py
    |- tests
    |- __init__.py
    |- manage.py
    |- README.rst
    |- requirements.txt
    |- venv
    |- requirements.txt

    你已经可以运行它试一下,但这时候只能看到一个404的错误页面:

    $ python -m bottle bottleapp
    

    讲真似乎真的没有构建项目的必要..

    视图函数和路由

    Bottle内置了一个强大的route引擎,可以给每个浏览器请求找到正确的回调函数。

    先来看一个小小的Hello World:

    1
    2
    3
    4
    5
    from bottle import route,run
    @route('/hello')
    def hello():
    return "Hello World!"
    run(host='localhost', port=8080,debug=True)

    在浏览器请求一个URL时,框架自动调用与之相应的函数,将函数的返回值发送给浏览器。这里route()函数将“/hello”这个URL地址绑定到“hello()”这个函数上,任何对“/hello”这个URL的请求都被递交到这个函数中。

    run()启动了内置的开发服务器,把指定的域名和端口作为参数传入。它监听localhost的8080端口并响应请求.但它不能满足生产环境的需求。

    Bottle的这种URL地址映射方法其实与flask差不多,也是使用了装饰器将函数和URL进行绑定的方法。

    上面的route()函数将一个URL路径与一个回调函数关联起来,然后在默认应用中添加了一个URL映射(route).你也可以在你的应用中多添加几个路由器。因为一个回调函数可绑定多个route。

    1
    2
    3
    4
    @route('/')
    @route('/hello/<name>')
    def greet(name='Stranger'):
    return 'Hello %s!' % name

    如果有一个URL没有被绑定到任何回调函数上,Bottle将返回“404 Page Not Found”的错误页面。

    上面的栗子使用了模块层面的route()装饰器函数来定义route,这样,所有route都会添加到一个全局的”默认应用”,它是一个Bottle的实例,第一次调用route()时候会自动创建。

    这使其他几个模块层面的修饰器函数都与这个”默认应用”有关.为了避免使用全局范围的”默认应用”,我们可以创建一个独立的应用对象.**

    1
    2
    3
    4
    5
    6
    from bottle import Bottle,run
    app = Bottle() #创建了一个Bottle对象app,然后所有函数都会映射到app的URL地址上
    @app.route('/hello')
    def hello():
    return "Hello World!"
    run(app, host='localhost', port=8080)

    这样别人就可以安全地导入你的app,然后通过Bottle.mount()方法合并到他的应用中。

    调试模式:

    bottle.debug(True)
    

    自动重载:

    1
    2
    from bottle import run
    run(reloader=True)

    动态路由:

    动态路由就是有通配符的路由,它能匹配多个URL地址.URL中的通配符会当作参数传给回调函数,直接在回调函数中使用.在同一个route里面,这个变量名需要是唯一的。

    1
    2
    3
    @route('/hello/<name>')  #旧语法中为:name
    def hello(name = 'World'): #这里定义了一个默认参数
    return 'Hello {}!'.format(name)

    过滤器:

    过滤器(Filter)可被用来定义特殊类型的通配符。在通配符传递给回调函数前,先自动转换通配符类型.包含过滤器的通配符定义一般像这样.config部分可选,由被使用的过滤器决定。以下是几种过滤器:

    :int 匹配一个整形,自动将其转换为int类型.
    :float 匹配一个浮点数,自动将其转换为float
    :path 匹配所有字符,包含”/“
    :re[:config] 允许在config中写一个正则表达式.

    HTTP请求方法

    在Bottle中,未指明请求访问的路由会默认使用GET方法.要处理如POST,PUT或者DELETE等等的其它请求,必须主动地在route()函数中添加它们,或者使用装饰器:@get(),@post()等等.

    举一个用POST方法实现用户登录的栗子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from bottle import get, post, request
    @get('/login') #或@route('/login', method = 'GET')
    def login_form():
    return '''<form method="POST" action="/login">
    <input name="name" type="text" />
    <input name="password" type="password" />
    <input type="submit" />
    </form>'''

    @post('/login') #或@route('/login', method = 'POST')
    def login_submit():
    name = request.forms.get('name')
    password = request.forms.get('password')
    if check_login(name, password):
    return '<p>Your login was correct</p>'
    else:
    return '<p>Login failed</p>'

    在这个栗子中,/login绑定了两个回调函数,一个回调函数响应GET请求,另一个响应POST请求。若浏览器用GET请求访问/login,则调用login_form()来返回登录界面,浏览器用POST方法提交表单后,调用login_submit()函数来检查用户有效性,并返回登录结果。

    静态文件
    Bottle内置的服务器不会自动处理像图片或CSS文件的静态文件请求。你需要给静态文件提供一个路由(告诉服务器哪些文件需要服务),一个回调函数(用来查找和控制静态文件的访问。)

    1
    2
    3
    4
    from bottle import static_file
    @route('/static/<filename>')
    def server_static(filename):
    return static_file(filename,root='/path/to/your/static/files')

    static_file()函数用来返回静态文件请求,上面的示例中,我们只返回”/path/to/your/static/files”路径下的文件,如果我们想要响应“/path/to/your/static/files”目录的子目录下的文件请求,那么我们可以使用一个格式化的通配符:

    1
    2
    3
    @route('/static/<filepath:path>')
    def server_static(filepath):
    return static_file(filepath, root='/path/to/your/static/files')

    使用root=’./static/files’这样的相对路径时,注意当前工作目录(./)不一定是项目文件夹。

    错误页面
    如果任何请求的URL没有的到匹配的回调函数,Bottle会返回一个默认的错误页面,提供足够的debug信息。你也可以用error()设置自己的相关回调函数,自定义错误页面:

    1
    2
    3
    4
    from bottle import error
    @error(404)
    def error404(error):
    return 'Nothing here, sorry!'

    这里传给error404函数的唯一参数是一个HTTPError对象的实例。除此,这个回调函数与我们用来响应普通请求的回调函数没有不同。你可以从request中读取数据,往response中写入数据和返回所有HTTPError支持的数据类型。
    只有在你的应用返回或raise一个HTTPError异常的时候,处理Error的函数才会被调用。更改Request,status或返回HTTPResponse不会触发错误处理函数。

    前端模板

    Bottle内置了一个快速且强大的模板引擎,SimpleTemplateEngine(stpl)。你可以使用template()函数或者view()装饰器来渲染一个模板,(这两个函数默认调用的模板引擎就是SimpleTemplate)你只要提供模板的名字和传递给模板的变量,下面是一个简单的栗子:

    1
    2
    3
    4
    @route('/hello')
    @route('/hello/<name>'):
    def hello(name='World'):
    return template('hello_template', name=name)

    这将加载hello_template.tpl模板文件,并提供name变量,并渲染它,再将结果返回给浏览器。默认情况,Bottle会在./views/查找模板文件。你可以在bottle.TEMPLATE_PATH这个列表中添加模板路径。

    view()装饰器允许你在回调函数中返回一个字典,并将其传递给模板。来一个栗子:

    1
    2
    3
    4
    5
    @route('/hello')
    @route('/hello/<name>')
    @view('hello_template')
    def hello(name='World'):
    return dict(name=name)

    SimpleTemplate的语法

    python对空白敏感的语法使它很难作为一个模板语言。SimpleTemplate移除了一些限制。SimpleTemplate模板会被编译为python字节码,且在每次通过SimpleTemplate.render()渲染时执行。
    注意:编译模板和渲染模板是两件事。通常模板只会被编译一次,然后会被缓存起来,但是会根据不同的参数,被多次渲染。

    内嵌语句
    只要在括号中的python语句返回一个字符串或有一个字符串的表达形式,它就是一个有效的语句。

    1
    2
    3
    4
    5
    6
    >>> template('Hello {{name}}!', name='World')
    u'Hello World!'
    >>> template('Hello {{name.title() if name else "stranger"}}!', name=None)
    u'Hello stranger!'
    >>> template('Hello {{name.title() if name else "stranger"}}!', name='mArC')
    u'Hello Marc!'

    括号中的python语句会在渲染的时候被执行,可访问传递给SimpleTemplate.render()方法的所有参数。默认情况下,它会自动转义HTML标签以防止XSS攻击。可在语句前加上“!”来关闭自动转义。

    1
    2
    3
    4
    >>>template('Hello {{name}}!', name='<b>World</b>')
    u'Hello &lt;b&gt;World&lt;/b&gt;!'
    >>>template('Hello {{!name}}!', name='<b>World</b>')
    u'Hello <b>World</b>!'

    嵌入python代码

    这个模板的语法类似于python的语法,它会确保语句块的正确缩进,所以你在写模板时不用担心缩进问题。

    1
    2
    3
    4
    5
    6
    7
    %if name == 'World':
    <h1> Hello {{name}} </h1>
    <p> This is a test.</p>
    %else:
    <h1>Hello {{name.title()}}!</h1>
    <p>How are you?</p>
    %end

    一行以%开头,表明这一行是python代码。它和真正的python代码唯一的区别,在于你需要显式地在末尾添加%end语句,表明一个代码块结束。这样你就不必担心python代码中的缩进问题,SimpleTemplate模板引擎帮你处理了。不以%开头的行被当作普通文本来渲染。

    只有在行首的%字符才有意义,可以使用%%来转义。

    注意:模板在经过编译后会缓存在内存中,所以你在修改模板文件后,要调用bottle.TEMPLATES.clear()函数手工清除缓存,才能看到效果。而在debug模式下,缓存会被禁用,于是就无需手动清除缓存。

    防止换行
    在一行代码前面加上\来防止换行。

    1
    2
    3
    4
    5
    <span>\\
    %if True:
    nobreak\\
    %end
    </span>

    该模板会输出:

    <span>nobreak</span>
    

    模板继承

    • %include语句

    可使用%include sub——template[kwargs]语句来包含其他模板。sub_template参数是模板的文件名或路径。[kwargs]部分是以逗号分开的键值对,是传给其他模板的参数。

    1
    2
    3
    %include header_template title='Hello World!'
    <p>Hello World</p>
    %include footer_template
    • %rebase语句

    %rebase base_template [kwargs]语句会渲染base_template这个模板,而不是原先的模板。然后base_template中使用一个空%include语句来包含原先的模板,并可访问所有通过kwargs传过来的参数。这样就可用模板来封装另一个模板,或是模拟引擎中的继承机制。**
    假设现在有一个与内容有关的模板,想在它上面加一层普通的HTML层。为了避免include一堆模板,可以用一个基础模板。

    名为layout.tpl的基础模板

    1
    2
    3
    4
    5
    6
    7
    8
    <html>
    <head>
    <title>{{title or 'No title'}}</title>
    </head>
    <body>
    %include
    </body>
    </html>

    名为 content.tpl的主模板

    1
    2
    This is the page content:{{content}}
    %rebase layout title='Content Title'

    然后来渲染content.tpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> print template('content', content='Hello World!')

    <html>
    <head>
    <title>Content Title</title>
    </head>
    <body>
    This is the page content:Hello World!
    </body>
    </html>

    模板内置函数
    在模板中访问一个未定义的变量会导致NameError异常,并立即终止模板的渲染。在抛出异常前,你无法检查变量是否被定义。当你想让输入更灵活或想在不同情况下使用同一个模板时,就很烦人了。SimpleTemplate模板引擎内置了三个函数来解决这个问题。

    defined(name):如果变量已定义则返回True,反之返回False
    get(name,default=None):返回该变量,或一个默认值
    setdefault(name,default):如果该变量未定义,则定义它,赋一个默认值,返回该变量

    下面这个栗子使用了这些函数,实现了模板中的可选参数:

    1
    2
    3
    4
    5
    6
    % setdefault('text','No Text')
    <h1>{{get('title','No Title')}}</h1>
    <p> {{ text }} </p>
    % if defined('author'):
    <p>By {{ author }}</p>
    % end

    数据库

    Bottle没有orm,没有专门封装数据库操作,不能直接支持数据库,需要通过一些插件实现。

    如果你确实想在你的应用里使用数据库,寻求ORM支持,你可以选择使用SQLAlchemy,PyMongo,MongoEngine, CouchDB等。

    所以如果你需要开发基于数据库的网站,且数据库的结构不会经常变化,那最好使用大型Python Web框架。而如果你的网站使用的是关系数据库,就可以不使用大型框架,直接用bottle、flask这类框架结合关系数据库模块就行。

    Key/Value数据库
    Bottle通过bottle.db模块变量提供一个key/value数据库.存储的对象类似dict字典,keys和values必须是字符串.不支持items(),values()这些方法.

    bottle-mysql

    $ pip install bottle-mysql
    

    “Bottle-MySQL is a plugin that integrates MySQL with your Bottle application. It automatically connects to a database at the beginning of a request, passes the database handle to the route callback and closes the connection afterwards.”
    “Bottle-MySQL是一个能够将你的应用和MySQL整合起来的插件。它在请求开始的时自动地连接上数据库,通过数据库句柄到路由回调,最后关闭连接。”

    1
    2
    3
    4
    5
    6
    import bottle
    import bottle_mysql

    app = bottle.Bottle()
    plugin = bottle_mysql.Plugin(dbuser='user', dbpass='pass', dbname='db')
    app.install(plugin)

    你也可以直接用python的数据库模块MYSQLdb来连接MYSQL,直接import MYSQLdb就可以。

    Bottle-SQLite

    这个插件让在Bottle应用中使用sqlite数据库变得简单。你只要在route的回调函数里添加一个‘db’参数,就能使用数据库链接了。

    $ pip install bottle-sqlite
    

    API编写与参考

    也是跟flask差不多,不同的是它可以把HTTP请求方法作装饰器用.

    一个简单的栗子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from bottle import request, response
    from bottle import post, get, put, delete

    _names = set() # the set of names

    @post('/names')
    def creation_handler():
    '''Handles name creation'''
    pass

    @get('/names')
    def listing_handler():
    '''Handles name listing'''
    pass

    @put('/names/<name>')
    def update_handler(name):
    '''Handles name updates'''
    pass

    @delete('/names/<name>')
    def delete_handler(name):
    '''Handles name deletions'''
    pass

    也可以单独创建一个API文档,将上面这段代码保存为names.py,然后再建立一个main.py文件。

    1
    2
    3
    4
    5
    6
    7
    import bottle
    from api import names

    app = application = bottle.default_app()

    if __name__ == '__main__':
    bottle.run(host = '127.0.0.1', port = 8000)

    参考
    API参考
    API Reference

    项目部署

    Bottle默认运行在它内置的随python一起发布的WSGI reference Server服务器上。这个单线程的HTTP服务器在开发时很有用,但是性能较低.
    最简单的增加性能的办法是安装一个多线程的服务器库,例如paste或者cherrypy ,并告诉 Bottle 使用它来替代单线程的服务器:

    bottle.run(server='paste')
    

    默认地,Bottle会监听127.0.1的8080端口,如果想更改它,就更改run函数的参数。当Bottle运行在其他服务器上时,port和host参数依然适用。

    run(port=80,host='123.45.67.89')
    

    因为这个服务器是单线程的,一次只能响应一个请求,可是Bottle已经可以工作在很多多线程的服务器上了,所以还是建议在大型项目上使用高性能服务器。

    1
    2
    3
    Bottle v0.12.9 server starting up (using WSGIRefServer())...
    Listening on http://127.0.0.1:8080/
    Hit Ctrl-C to quit.

    其他特性(待加入)

    Session

    Bottle自身没有提供Session(处理用户在多次请求之间需要存储的数据)的支持,但可以使用beaker中间件来实现。
    Beaker是一个缓存和会话的库,与Web应用程序和独立的Python脚本及应用程序一起使用。它是WSGI的中间件,能够很简单地嵌入,与基于WSGI的Web应用程序一起使用,并且缓存修饰器对于任何基于Python的应用程序都是容易使用的。

    $ pip install beaker
    

    栗子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from bottle import route, run ,redirect,request,default_app
    from beaker.middleware import SessionMiddleware
    #设置session参数(Session数据存放在'/tmp/'目录里面,/tmp/有个机制,会自动清理30天后末使用过的文件,有点类型于回收站,所以你不需要担心session文件不断增多的问题。)
    session_opts = {
    'session.type':'file', # 以文件的方式保存session
    'session.cookei_expires':3600, # session过期时间为3600秒
    'session.data_dir':'/tmp/sessions', # session存放路径
    'sessioni.auto':True
    }

    app = SessionMiddleware(bottle.app(), session_opts)

    @bottle.route('/test')
    def test():
    s = bottle.request.environ.get('beaker.session') #获取session,则获取环境变量中的beaker.session对象,并赋值给s,然后我们就可以用字典的方式,往s里面添加一些我们要存进去的数据,如帐号名,帐号id,权限等等
    s['test'] = s.get('test', 0)+1 #从session中获取Key为test的值
    s.save()
    return 'Test counter: %d' % s['test']

    bottle.run(app=app,host='0.0.0.0', port=8080,debug=True)

    一些推荐的可用的插件

    Bottle-Beaker:Beaker to session and caching library with WSGI Middleware
    Bottle-Cork:Cork provides a simple set of methods to implement Authentication and Authorization in web applications based on Bottle.
    Bottle-Extras:Meta package to install the bottle plugin collection.
    Bottle-Flash:flash plugin for bottle
    Bottle-Hotqueue:FIFO Queue for Bottle built upon redis
    Macaron:Macaron is an object-relational mapper (ORM) for SQLite.
    Bottle-Memcache:Memcache integration for Bottle.
    Bottle-Mongo:MongoDB integration for Bottle
    Bottle-Redis:Redis integration for Bottle.
    Bottle-Renderer:Renderer plugin for bottle
    Bottle-ServefilesA reusable app that serves static files for bottle apps
    Bottle-Sqlalchemy:SQLAlchemy integration for Bottle.
    Bottle-Sqlite:SQLite3 database integration for Bottle.
    Bottle-Web2pydal:Web2py Dal integration for Bottle.
    Bottle-Werkzeug:Integrates the werkzeug library (alternative request and response objects, advanced debugging middleware and more)

    Bottle学习资源

    • PS:因为Bottle真的太小了,以至于网上关于它的资料也比较少,而且基本上都是差不多的,感觉官方文档也不是很全面,相关的书也没有,于是只能大量百度和google来搜集资料学习。

    Bottle: Python Web Framework
    Bottle v0.11中文文档
    简单介绍Python的轻便web框架Bottle
    bottle框架基础教程
    Bottle web 开发 (视频)
    几个bottle插件
    bottle-mysql 0.1.4
    Python Web框架
    Bottle手册(0.13-dev)中文翻译
    bottle 源码解析
    初窥Bottle
    Python程序员都会喜欢的6个库
    bottle中文
    微型Python Web框架 Bottl
    bottle中文教程
    Python的Bottle框架的一些使用技巧介绍
    Bottle框架常见的几个坑
    Bottle API 参考 中文
    Where to start a project with bottle, Boilerplate
    Python3 bottle Web开发系列教程
    Building a Rest API with the Bottle Framework
    Developing With Bottle
    Bottle API 参考

    文章目录
    1. 1. 简介
    2. 2. 关于安装
    3. 3. 项目构建
    4. 4. 视图函数和路由
      1. 4.1. 动态路由:
    5. 5. 前端模板
      1. 5.0.1. SimpleTemplate的语法
  • 6. 数据库
  • 7. API编写与参考
  • 8. 项目部署
  • 9. 其他特性(待加入)
    1. 9.1. Session
    2. 9.2. 一些推荐的可用的插件
  • 10. Bottle学习资源