1. 程式人生 > >很好用的Python框架Bottle

很好用的Python框架Bottle

Bottle 是一個非常小巧但高效的微型 Python Web 框架,它被設計為僅僅只有一個檔案的Python模組,並且除Python標準庫外,它不依賴於任何第三方模組。

  • 路由(Routing):將請求對映到函式,可以建立十分優雅的 URL
  • 模板(Templates):Pythonic 並且快速的 Python 內建模板引擎,同時還支援 mako, jinja2, cheetah 等第三方模板引擎
  • 工具集(Utilites):快速的讀取 form 資料,上傳檔案,訪問 cookies,headers 或者其它 HTTP 相關的 metadata
  • 伺服器(Server):內建HTTP開發伺服器,並且支援 paste, fapws3, bjoern, Google App Engine, Cherrypy 或者其它任何 WSGI
     HTTP 伺服器

安裝 Bottle

正如上面所說的, Bottle 被設計為僅僅只有一個檔案,我們甚至可以不安裝它,直接將 bottle.py 檔案下載並複製到我們的應用中就可以使用了,這是一個好辦法,但是如果還是想將其安裝,那麼我們可以像安裝其它的 Python 模組一樣:

sudo easy_install -U bottle

如果我們直接將 bottle.py 下載到自己的應用中的話,我們可以建立下面這樣的目錄結構:

+ application
+----bottle.py
+----app.py

我們可以將下面的建立 Bottle 例項的示例程式碼複製到 app.py 檔案中,執行該檔案即可。

示例:Bottle 的 “Hello World” 程式

下面的程式碼我們建立了一個十分簡單但是完整的 Bottle 應用程式(在Python Consle)中:

>>> from bottle import route, run
>>> @route('/hello/:name')
... def index(name = 'World'):
...     return '<strong>Hello {}!'.format(name)
... 
>>> run(host='localhost',port=8080)
Bottle server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Use Ctrl-C to quit.

在 Python Consle中輸入上面的程式碼,我們就得到了一個最簡單但完整的 Web 應用,訪問:“http://localhost:8080/hello/bottle”試試。上面到底發生了什麼?

  1. 首先,我們匯入了兩個 Bottle 的元件, route() Decorator 和 run() 函式
  2. route() 可以將一個函式與一個URL進行繫結,在上面的示例中,route 將 “/hello/:name’ 這個URL地址繫結到了 “index(name = ‘World’)” 這個函式上
  3. 這個是一個關聯到 “/hello” 的 handler function 或者 callback ,任何對 “/hello” 這個URL的請求都將被遞交到這個函式中
  4. 我們獲得請求後,index() 函式返回簡單的字串
  5. 最後,run() 函式啟動伺服器,並且我們設定它在 “localhost” 和 8080 埠上執行

上面這種方法僅僅只是展示一下下 Bottle 的簡單,我們還可以像下面這樣,建立一個 Bottle 物件 app,然後我們會將所有的函式都對映到 app 的 URL 地址上,如上示例我們可以用下面這種辦法來實現:

from bottle import Bottle, run
app = Bottle()
@app.route('/hello')
def hello():
    return "Hello World!"
run(app, host='localhost', port=8080)

Bottle 的這種 URL 地址對映方法與我一直使用的 Flask 的地址對映方法很相似,到現在為止,我似乎只看到它們只是語法上面有些話不同。

路由器(Request Routing)

Bottle 應用會有一個 URL 路由器,它將 URL 請求地址繫結到回撥函式上,每請求一些 URL,其對應的 回撥函式就會執行一些,而回調函式返回值將被髮送到瀏覽器,你可以在你的應用中通過 route() 函式新增不限數目的路由器。

from bottle import route
@route('/')
@route('/index.html')
def index():
    return '<a href="/hello">Go to Hello World Page</a>'
@route('/hello')
def hello():
    return 'Hello World'

就像你看到,你所發出的訪問請求(URL),應用並沒有返回伺服器上真實的檔案,而是返回與該URL繫結的函式的返回值,如果其一個URL沒有被繫結到任何回撥函式上,那麼 Bottle 將返回“404 Page Not Found” 的錯誤頁面

動態路由(Dynamic Routes)

Bottle 有自己特有的 URL 語法,這讓我們可以很輕鬆的在 URL 地址中加入萬用字元,這樣,一個 route 將可以對映到無數的 URL 上,這些動態的 路由常常被用來建立一些有規律性的內容頁面的地址,比如部落格文章地址“/archive/1234.html” 或者 “/wiki/Page_Title”,這在上面的示例我已經演示過了,還記得嗎?

@route(’/hello/:name’)
def hello(name = ‘World’): return ‘Hello {}!’.format(name)

上面的路由器,可以讓我們通過“/hello/costony”或者“/hello/huwenxiao”等地址來訪問,而 Bottle 返回的內容將是“Hello costony!” 或者 “Hello huwenxiao!”,“/hello/”之後的字串交被返回來,預設的萬用字元將匹配所有下一個“/”出現之前的字元。我們還可以對萬用字元進行格式化:

@route('/object/:id#[0-9]+#')
def view_object(id):
    return 'Object ID: {}'.format(id)

上面的路由將只允許 id 為由數字“0-9”組成的數字,而其它的字串都將返回 404 錯誤。

HTTP 請求方法(Request Methods)

HTTP 協議為不同的需求定義了許多不同的請求方法,在 Bottle 中,GET方法將是所有未指明請求訪問的路由會預設使用的方法,這些未指明方法的路由都將只接收 GET 請求,要處理如 POSTPUT 或者 DELETE 等等的其它請求,你必須主動地在 route() 函式中新增method 關鍵字,或者使用下面這些 decorators:@get()@ , post() , put() , delete() 。

POST 方法在經常被用來處理 HTML 的表單資料,下面的示例演示了一個 登陸表單的處理過程:

from bottle import get, post, request
#@route('/login')
@get('/login')
def login_form():
    return '''<form method = "POST">
                <input name="name" type="text" />
                <input name="password" type="password" />
                <input type="submit" value="Login" />
                </form>'''
#@route('/login', method = 'POST')
@post('/login')
def login():
    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>'

在上面的示例中,@/[email protected] 被繫結到兩個不同的回撥函式上,一個處理 GET 請求,另一個處理 POST 請求,第一個返我們的登陸表單,第二個接收登陸表單提交的資料,並進行處理,得到結果後,返回結果。

自動回退(Automatic Fallbacks)

特殊的 HEAD 方法,經常被用來處理一些僅僅只需要返回請求元資訊而不需要返回整個請求結果的事務,這些HEAD方法十分有用,可以讓我們僅僅只獲得我們需要的資料,而不必要返回整個文件,Bottle 可以幫助我們很簡單的實現這些功能,它會將這些請求對映到與URL繫結的回撥函式中,然後自動擷取請求需要的資料,這樣一來,你不再需要定義任何特殊的 HEAD 路由了。

靜態檔案路由(Routing Static Files)

對於靜態檔案, Bottle 內建的伺服器並不會自動的進行處理,這需要你自己定義一個路由,告訴伺服器在哪些檔案是需要服務的,並且在哪裡可以找到它們,我們可以寫如下面這樣的一個路由器:

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” 路徑下的檔案,因為 :filename 萬用字元並不接受任何 “/” 的字元,如果我們想要“/path/to/your/static/files” 目錄的子目錄下的檔案也被處理,那麼我們可以使用一個格式化的萬用字元:

@route('/static/:path#.+#')
def server_static(path):
    return static_file(path, root='/path/to/your/static/files')

錯誤頁面(Error Pages)

如果任何請求的URL沒有的到匹配的回撥函式,那麼 Bottle 都會返回錯誤頁面,你可以使用 error() decorator 來抓取 HTTP 狀態,並設定自己的相關回調函式,比如下面我們的處理404錯誤的函式:

@error(404)
def error404(error):
    return '404 error, nothing here, sorry!'

這個時候,404 檔案未找到錯誤將被上面的自定義404錯誤處理方法代替,傳送給錯誤處理函式的唯一的一個引數是一個 HTTPError 例項,它非常將普通的 request,所以,你也可以有 request 中讀取到,也可以寫入 response 中,並且返回任何 HTTPError 支援的資料型別。

生成內容(Generating Content)

在純粹的 WSGI中,你的應用能返回的資料型別是十分有限的,你必須返回可迭代的字串,你能返回字串是因為字串是可以迭代的,但是這導致伺服器將你的內容按一字元一字元的傳送,這個時候,Unicode 字元將不允許被返回了,這是肯定不行的。

Bottle 則支援了更多的資料型別,它甚至添加了一個 Content-Length 頭資訊,並且自動編碼 Unicode 資料,下面列舉了 Bottle 應用中,你可以返回的資料型別,並且簡單的介紹了一下這些資料型別的資料都是怎麼被 Bottle 處理的:

資料型別 介紹
字典(Dictionaries) Python 內建的字典型別資料將自動被轉換為 JSON 字串,並且新增 Content-Type 為 ’application/json’ 的頭資訊返回至瀏覽器,這讓我們可以很方便的建立基於 JSON 的API
空字串,False,None或者任何非真的資料 Bottle 將為這類資料建立 ContentLength 標頭檔案,被設定為 0 返回至瀏覽器
Unicode 字串 Unicode 字串將自動的按 Content-Type 標頭檔案中定義的編碼格式進行編碼(預設為UTF8),接著按普通的字串進行處理
位元組串(Byte strings) Bottle 返回整個字串(而不是按位元組一個一個返回),同時增加 Content-Length 標頭檔案標示位元組串長度
HTTPError 與HTTPResponse 例項 返回這些例項就像丟擲異常一樣,對於 HTTPError,錯誤將被與相關函式處理
檔案物件 然後具有 .read() 方法的物件都被看作檔案或者類似檔案的物件進行處理,並傳送給 WSGI 伺服器框架定義wsgi.file_wrapper 回撥函式,某一些WSGI伺服器會使用系統優化的請求方式(Sendfile)來發送檔案。
迭代器與生成品 你可以在你的回撥函式使用 yield 或者 返回一個迭代器,只要yield的物件是字串,Unicode 字串,HTTPError 或者 HTTPResponse 物件就行,但是不允許使用巢狀的迭代器,需要注意的是,當 yield 的值第一次為非空是, HTTP 的狀態 和 標頭檔案將被髮送到 瀏覽器

如果你返回一個 str 類子類的例項,並且帶有 read() 方法,那它還是將按 字串進行處理,因為字串有更高一級的優先處理權。

改變預設編碼

Bottle 依照 Content-Type 標頭檔案中 charset 引數來對字串進行編碼,該標頭檔案預設為 text/html; charset=UTF8 ,並且可以被Response.content_type 屬性修改,或者直接被 Response.charset 屬性修改:

from bottle import response
@route('/iso')
def get_iso():
    response.charset = 'ISO-8859-15'
    return u'This will be sent with ISO-8859-15 encoding.'
@route('/latin9')
def get_latin():
    response.content_type = 'text/html; charset=latin9'
    return u'ISO-8859-15 is also known as latin9.'

由於某些罕見的原因,Python 編碼的名稱可能與 HTTP 編碼的名稱不一致,這時你需要做兩方法的工作首先設定Response.content_type 標頭檔案,然後還需要設定 Response.charset 。

靜態檔案

你可以直接返回檔案,但是 Bottle 推薦使用 static_file() 方法,它會自動的猜測檔案的 mime-type,追加 Last-Modified 標頭檔案,完全的自定義需要服務的檔案路徑,並且能處理錯誤(比如 404),並且它還支援 If-Modified-Since 標頭檔案並且可以返回 304 Not Modified 響應,你還可以使用一個自定義的 mime-type 來重寫 mime-type 猜測的值。

from bottle import static_file
@route('/images/:filename#.*\.png#')
def send_image(filename):
    return static_file(filename, root='/path/to/image/files', mimetype = 'image/png')
@route('/static/:filename')
def send_static(filename):
    return static_file(filename, root='/path/to/static/files')

如果你真的需要,你還可以以異常的形式丟擲檔案。

強制下載

絕大多數瀏覽器在知道下載的檔案的MIME型別並且該檔案型別被繫結到某一個應用程式時(比如PDF檔案),它們都會自動的開啟該檔案,如果你不想這樣,你可以強制的要求瀏覽器進行下載。

@route('/download/:filename')
def download(filename):
    return static_file(filename, root='/path/to/static/files', download=filename)

HTTP 錯誤與重定向

abort() 函式是建立 HTTP 錯誤頁面的快捷方式:

from bottle import route, abort
@route('/restricted')
def restricted():
    abort(401, 'Sorry, access denied.')

要將瀏覽器請求的地址重定向其它的地址,你可以向瀏覽器傳送一個 303 see other 響應, redirect() 可以實現這個功能:

from bottle import redirect
@route('/wrong/url')
def wrong():
    redirect('/right/url')

除了 HTTPResponse 或者 HTTPError 異常外,還會有 500 Internal Server Error 響應。

Response 例項

響應的無資料如 HTTP 狀態碼,響應標頭檔案,或者 Cookies 都被儲存在一個叫做 response 的物件中,並傳送給瀏覽器,你可以直接操作這些無資料或者寫一些預定義的 helper 方法來處理它們。

狀態碼(Status Code)

HTTP 狀態碼 控制著瀏覽器處理方式,預設為“200 OK”,絕大多數情況下,你並不需要手工的去設定 Response.status ,但是使用abort() 函式或者返回一個 HTTPResponse 物件的時候,因為它們允許存在任何數值的狀態碼,為了符合 HTTP 規範,我們應該手動的為其新增規範的 HTTP 狀態碼。

響應標頭檔案(Response Header)

響應的標頭檔案如 Cache-Control 或者 Location 等都是通過 @Response.set_header() 函式定義的,該函式接受兩個引數:一個頭檔名稱和一個值,名稱部分是區分大小寫的:

@route('/wiki/page')
def wiki(page):
    response.set_header('Content-Language', 'en')
    ...

絕大多數標頭檔案都僅僅只能定義一次,但是有一些特別的標頭檔案卻可以多次定義,這個時候我們在第一次定義時使用Response.set_header() ,但是第二次定義時,就需要使用 Response.add_header() 了:

response.set_header('Set-Cookie','name=value')
response.add_header('Set-Cookie','name1=value1')

Cookies

你可以使用 Request.get_cookie() 訪問已經設定了的 Cookie,可以使用 Response.set_cookie() 設定 Cookie:

@route('/hello')
def hello_again(self):
    if request.get_cookie('visited'):
        return 'Welcome back! Nice to see you again'
    else:
        response.set_cookie('visited','yes')
        return 'Hello there! Nico to meet you!'

Response.set_cookie() 方法接受一些特殊的引數,用來控制 Cookie 的生命週期或者行為,最常見的一些引數如下:

  • max_age : 該 Cookie 最大的生命期(按秒計算,預設為 None)
  • expires : 上個 datetime 物件或者一個 UNIX timestamp(預設為 None)
  • domain : 允許訪問該 Cookie 的域名(預設為當前應用的域名)
  • path : 按照路徑限制當前 Cookie(預設為 “/“)
  • secure : 限制當前Cookie僅僅允許通過 HTTPS 連線訪問(預設為 off)
  • httponly : 阻止瀏覽器端 Javascript 讀取當前 Cookie(預設為 off,需要 Python 2.6 以上)

如果 expires 或者 max_age 都沒有設定的放在, Cookie 將在瀏覽器的會話結束後或者當瀏覽器關閉時失效,這裡還有一些問題是你在使用 Cookie 時需要考慮到的:

  • 大多數瀏覽器都限制 Cookie 的大小不能超過 4Kb
  • 有一些使用者設定了他們的瀏覽器不接受任何 Cookie,絕大多數搜尋引擎也直接忽略 Cookie,你應該保證你的應用在沒有 Cookie 時也是可用的
  • Cookie 儲存在客戶端,並且沒有任何加密措施,你存放在 Cookie 中的任何內容,使用者都是可訪問的,如果有必要的話,攻擊者能通過 XSS 漏洞竊取使用者的 Cookie,所以,儘可能在不要在 Cookie 中儲存機密資訊
  • Cookie 是很容易被偽造的,所以,儘可能不要想信 Cookie

就像上面看到的, Cookie 太容易被惡意軟體盜取,所以 Bottle 為 Cookie 提供的加密方法,你所需要做的僅僅只是提供了一個金鑰,只要能確保該金鑰的安全即可,而其導致的結果是,對於未加密的 Cookie,@Request.get_cookie()@ 將返回 None。

@route('/login')
def login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_user_credentials(username, password):
        response.set_cookie('account', username, secret='some-sceret-key')
        return 'Welcome {}'.format(username)
@route('/restricted')
def restricted_area(self):
    username = request.get_cookie('account', secret='some-secret-key')
    if username:
        return 'Hello {}'.format(username)
    else:
        return 'You are not logged in.'

另外,Bottle 會自動 pickle 與 unpickle 你儲存到已簽名的 Cookie 上的資料,這表示你可以向 Cookie 中儲存任何可以 pickle 的資料物件,只要其大小不超過 4Kb即可。

訪問請求資料(Accessing Request Data)

Bottle 的全域性物件 request 提供了對 HTTP相關的無資料如 Cookies, Headers, 或者 POST 表單資料的訪問,該物件在任何時候都儲存著當前請求的資料,只要其在一個路由的回撥函式中訪問即可,它甚至還可以在多執行緒環境中工作。

HTTP 標頭檔案

標頭檔案資訊都儲存在 Request.header 中,其成員是一個鍵區分大小寫的 HeaderDict 例項:

from bottle import route, request
@route('js_ajax')
def is_ajax():
    if request.header.get('X-Requested-With') == 'XMLHttpRequest':
        return 'This is an AJAX request'
    else:
        return 'This is a normal request'

Cookies

Cookie 已一個普通的 dictionary 形式儲存在 Request.COOKIES 物件中, Request.get_cookie()@ 方法可以對簽名的 Cookie 進行訪問,下面示例展示了一個基於 Cookie 的訪問計數器:

from bottle import route, request, response
@route('/counter')
def counter():
    count = int( request.COOKIES.get('counter', '0'))
    count += 1
    response.set_cookie('counter',str(count))
    return 'You visited this page {} times'.format(count)

查詢字串(Query Strings)

查詢字串常常被用來傳遞一些小數目的鍵值對引數到伺服器,你可以使用 Request.GET 字典對其進行訪問,使用 Request.query_string來獲得整個字串:

from bottle import route, request, response
@route('/forum')
def display_forum():
    forum_id = request.GET.get('id')
    page = request.GET.get('page','1')
    return 'Forum ID: {} ( Page: {} )'.format(forum_id, page)

POST 表單資料與檔案上傳

POST 與 PUT 請求中, request 可以包含各種編碼方式的資料,使用 Request.forms 物件可以訪問普通的 POST 表單資料,檔案上傳時提交的資料被單獨以 cgi.FieldStorage 例項的形式儲存在 Request.files 中,而 Request.body 按原始資料的方式儲存有一個檔案物件的資料。

下面是一個檔案上傳的示例:

<form action"/upload" method="post" enctype="multipart/form-data">
    <input type="text" name="name" />
    <input type="file" name="data" />
    <input type="submit" value="Upload" />
</form>

Bottle 程式碼

from bottle import route, request
@route('/upload', method = 'POST')
def do_upload():
    name = request.forms.get('name')
    data = request.files.get('data')
    if name and data.file:
        raw = data.file.read() #當檔案很大時,這個操作將十分危險
        filename = data.filename
        return "Hello {}! You uploaded {} ({} bytes).".format(name, filename, len(raw))
    return "You missed a field"

WSGI 環境

Request 物件將 WSGI 環境資料都以 dictionary 等式儲存在 Request.environ 中,允許你像訪問字典資料一樣訪問其值:

route('/my_ip')
def show_ip():
    ip = request.environ.get('REMOTE_ADDR')
    # 或者 ip = request.get('REMOTE_ADDR')
    # 或者 ip = request['REMOTE_ADDR']
    return 'Your IP is : {}'.format(ip)

模板(Templates)

Bottle 內建了一個快速且強大的模板引擎,叫作:*SimpleTemplate Engine* ,你可以使用 template() 函式 或者 view()decorator 來編譯一個模板,你所要作的僅僅只是提供該模板,以及要傳送給模板的資料,下面是一個模板的簡單示例:

@route('/hello')
@route('/hello/:name'):
def hello(name = 'World')
    return template('hello', name = name)

上面的程式碼將載入 hello.tpl ,然後將 name 傳送給該模板,並編譯它,再將結果返回給瀏覽器, Bottle 將在 ./views/ 或者bottle.TEMPLATE_PATH 設定的路徑中搜索模板檔案。

view() decorator 允許你返回一組需要傳送給模板的資料字典即可,而不需要再重新傳送模板名稱:

@route('/hello')
@route('/hello/:name')
@view('hello')
def hello(name='World'):
    return dict(name=name)

模板語法

模板語法是非常精巧的,其工作原理基本可以說成是:將模板檔案中的程式碼進行正確的縮排處理,以至你不再需要擔心塊縮排問題:

%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

快取

模板被編譯之後會快取至記憶體中,你可以使用 bottle.TEMPLATES.clear() 去手工清除它們。

外掛(Plugins)

這是 Bottle 0.9 版本才有的新功能,外掛可以提供 Bottle 核心同有提供的功能集,在“可用的 Bottle 外掛列表”:http://bottlepy.org/docs/dev/plugins/index.html 中你可以找到現在可用的外掛,你還可以開發自己的 Bottle 外掛,比如 sqlite 外掛,可以讓你可以使用 db 來訪問一個到SQLite 資料的連結:

from bottle import route, install, template
from bottle_sqlite import SQLitePlugin
install(SQLitePlugin(dbfile='/tmp/test.db'))

route('/show/:post_id') def show(db, post_id): c = db.execute('SELECT title, content FROM posts WHERE id = ?', (int(post_id),)) row = c.fetchone() return template('show_post', title=row['title'], text=row['content'])route(’/contact’)
def contact_page(): ‘’‘該回調函式不需要任何資料庫連線,因為沒有 db 關鍵字, 所以 SQLite外掛將完全忽略該回調函式’‘’ return template(‘contact’)

在整個應用中安裝外掛

外掛可以被安裝到整個應用中,或者僅僅只針對某幾個路由安裝,絕大多數外掛都被安裝到整個應用中,以為所有路由服務。要安裝一個外掛,只需要將外掛的名稱作為第一個引數傳遞給 install() 函式即可:

from bottle_sqlite import SQLitePlugin
install(SQLitePlugin(dbfile='/tmp/test.db'))

解除安裝已安裝的外掛

你可以使用名稱,類或者物件來解除安裝一個已經安裝的外掛

sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db')
install(sqlite_plugin)
uninstall(sqlite_plugin) #解除安裝特定的外掛
uninstall(SQLitePlugin) #解除安裝該類的所的例項
uninstall('sqlite')          # 解除安裝所有具有該名稱的外掛
uninstall(True)            # 一次性解除安裝所有已安裝的外掛

外掛可以在任何時間安裝與解除安裝,甚至是處理某個請求的回撥函式中,每一次已經安裝的外掛樹更新時, 路由快取都會跟著更新。

與路由繫結的外掛安裝

route() 的 apply 引數可以指定某個回撥函式要安裝的外掛:

sqlite_plugin = SQLitePlugin(dbfile=’/tmp/test.db’)
@route(’/create’, apply=[sqlite_plugin])
def create(db): db.execute(‘INSERT INTO ….’)

外掛黑名單

如果可以使用 route() 方法中的 skip 引數指定外掛黑名單,如下:

sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db')
install sqlite_plugin)
@route('/open/:db', skip=[sqlite_plugin])
def open_db(db):
    if db in ['test','test2']:
        sqlite_plugin.dbfile = '/tmp/{}.db'.format(db)
        return 'Database File Switched to : /tmp/{}.db'.format(db)
    abort(404, 'No such database')

外掛與子應用

大多數外掛都被安裝到需要它的具體的應用中,所以,它們不應該影響註冊給Bottle 應用的子應用:

root = Bottle()
root.mount(apps.blog, '/blog')
@route.route('/contact', template='contact')
def contact():
    return {'email':'[email protected]')
root.install(plugins.WTForms())

上面的示例程式碼中,不管我們什麼時候 mount 一個子應用到主應用上,主應用都會為子應用設定一個代理,所以上面的 WTForms 外掛將只會影響到 ‘/contact’ 路徑,但是不會影響到 ‘/blog’ 子應用的所有URL。,但是這處理方式可以使用下面的方法覆蓋:

route.mount(apps.blog, '/blog', skip=None)

開發(Development)

上面已經介紹了一些基本的關於 Bottle 的知識,如果你現在想使用 Bottle 開發自己的應用,那麼下面這些技巧對於你的專案來說可能很有幫助:

預設應用

Bottle 維護著一份 Bottle 例項的棧,而 route() 其實是對 Bottle.route() 的快捷訪問,以這種方法產生的路由都屬於預設應用:

@route('/')
def hello():
    return 'Hello World'

對於小應用來說,這已經足夠了,但是隨著應用的不斷增大,這種方法顯然不容易維護,所以我們可以使用子應用,將整個專案的功能細分:

blog = Bottle()
@blog.route('/')
def index():
    return 'This is blog Index page'

將應用分離之後,程式的維護性提高了很多,而且可重用性也提高很多,其它的開發人員就可以放心的從你的模組中匯入應用程式物件,並使用 Bottle.mount() 將你的應用與他們的應用整全到一起。另外一種替代方法,你可以使用 應用棧 ,這讓你可以在所有子應用中都使用預設的 route 方法:

default_app.push()
@route('/')
def hello():
    return 'Hello World'
app = default_app.pop()

app() 與 default_app() 都是 AppStack 的例項,並且實現的類 Stack的API,你可以 Push 或者 Pop應用到這個 stack 中。

Debug 模式

在開發的前期,Debug 模式將非常有助於你的開發:

bottle.debug(True)

在這種模式下,Bottle 可以提供更多的 debugging 資訊,即使程式出現一個錯誤,它同時還關閉了一些優化功能,添加了一些配置的檢測功能,下面是該模式不完整的功能列表:

  • 預設錯誤頁面將返回一個對該錯誤的跟蹤
  • 模板不會被快取
  • 外掛將立即被安裝

自動過載

在開發的過程,你可能需要經常修改你的程式碼,又經常需要重啟你的伺服器以更新這些修改,Bottle 提供了一個自動過載的工具,這使得你對任何一個應用中的檔案的修改都會被及時的更新到執行中的應用中:

from bottle import run
run(reloader=True)

reloader 是這麼工作的: 主程序並不會啟動伺服器,但是它會按照同樣的引數建立一個子程序,這使得所有模組級的程式碼都會被執行兩次。子程序的執行環境中會有一個叫作 os.environ['BOTTLE_CHILD'] = True 的引數,當任何一個已經載入的模組有修改時,子程序會被停止,然後由主程序重新開啟新的子程序,對模板的修改將不會引發一次過載。

過載是基於是否可以關閉子程序的,如果你執行在 Windows 或者任何其它不支援 signal.SIGINT 的作業系統上時,@[email protected] 被用來終止子程序。

部屬(Deployment)

Bottle 預設是執行在內建的 wsgiref WSGIServer上的,該無執行緒伺服器對於開發來說再好不過了,但是對於日漸壯大的應用或者對於實際部屬來說,並不是最好的選擇。

多執行緒伺服器

提高效率的最快速的辦法,就是將應用部屬到一個多執行緒的伺服器或者類似 Asynchronous WSGI 的伺服器上,比如 paste 或者 cherrypy ,並且告訴 Bottle 以這些伺服器啟動,而不是自己內建的伺服器。

bottle.run(server='paste')

Bottle 支援很多伺服器,下面列舉的並不是所有的:

完整的伺服器名稱可使用 server_names 變數獲得,如果 Bottle 還沒有提供你最喜歡的伺服器,那你可以手工的使用你的伺服器啟動它:

from paste import httpserver
httpserver.serve(bottle.default_app(), host='0.0.0.0', port = 80)

多伺服器程序

一個 Python 程序只能使用到一個 CPU,即時伺服器硬體有多個CPU,你可以在不同的埠中啟動多個應用,每一個應用使用一個 CPU,然後使用分流伺服器對訪問進行分流,比如 Apache mod_wsgi 或者 Nginx 等都可以作為前端分流伺服器。

相關推薦

Python框架Bottle

Bottle 是一個非常小巧但高效的微型 Python Web 框架,它被設計為僅僅只有一個檔案的Python模組,並且除Python標準庫外,它不依賴於任何第三方模組。 路由(Routing):將請求對映到函式,可以建立十分優雅的 URL模板(Templates):Pythonic 並且快速的 Pyt

大家一起學python-day4-簡單的字串格式化("{}")

#format的第一個用法 #1.1普通的功能 msg = '大家好我叫{},今年{}歲' print(msg.format('阿衰',20))#大家好我叫阿衰,今年20歲 #1.2按照括號中的數字大小進行賦值,從0開始 msg = '大家好我叫{1},今年{0}歲,明年{0}歲' prin

python的時間處理包 Arrow

py標準包datetime 中的時間處理,方法較繁多,不是很好記憶,所以在使用的便利程度上難免會打個折,然後發現一款很好用的python的時間處理包Arrow git上的專案地址 簡單的

gooreplacer

acer stack rep row flow cer replace 喜歡 畫面 國內上 StackOverflow, hackernews 之類的站點會慢。 因為頁面裏有鏈接指向 goolge, 會被墻。 於是拖累了整個頁面的顯示。 gooreplacer 可以把這

的log4j

.cn mage com log4j image idt bsp blog 技術分享 很好用的log4j

的谷歌字體以及Gravatar頭像一鍵替換WordPress插件----WP Acceleration for China 插件

ati 以及 content none 應對 集合 .org ref 多余 WordPress總是被新上手的朋友詬病說速度慢,其實多半都要歸功於谷歌字體的功勞。在應對字體這個問題的時候,大家都會有各種不同的解決方案。今天我給大家推薦一款插件,它集合了多個替代方案,可以方便的

C語言

好用 store ejb wot gin userinfo mcs sina cbe JW83HX撓9墑JZ揭嶄http://shufang.docin.com/xbgoz81833 8PEyr4辛訪枚扒3http://jz.docin.com/hhbo9279 190k2

static (php)

靜態變量 簡單 技術 images lin target -- functions es2017 一、靜態變量具有這樣的特性: 當在某函數裏定義一個靜態變量後,這個變量不會即使函數退出了,在下次調用這個函數時,它會使用前次被調用後留下的值。 (這樣就不用把一個變

jstat 監控調整GC

mman 版本 capacity sta 52.0 最小 容量 ring keyword jstat命令使用 jstat命令可以查看堆內存各部分的使用量,以及加載類的數量。命令的格式如下: jstat [-命令選項] [vmid] [間隔時間/毫秒] [查詢次數] 註意:使

一個的自動生成工具——mybatis generator

led ron 很好 user runtime rim mod 文件 path mybatis generator-自動生成代碼 準備材料:   一個文件夾,一個數據庫的驅動包,mybatis-generator-core-1.3.5.jar,一條生成語句   如圖:(我用

一個的在線編輯、展示、分享、交流JavaScript 代碼的平臺

找到 png ron bubuko eight 就會 很好 str 技術分享 在發表博客時,有一些代碼只能粘貼進去,而不能看到代碼運行的效果,需要讀者把代碼粘貼進自己的編輯器,然後再運行看效果,這是一件很耗時的事情 在平時百度的時候,我發現一些網站可以在線預覽功能,而且可以

layerdate一款日期插件

可選 itl 選擇 render 元素 charset scrip head layer <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>l

高精度大數c++類模板

details lean 代碼 sprintf printf span tdi 我只 tar 首先聲明這是大佬寫的,我只是記錄下,拿來學習。附上大佬的鏈接 : https://blog.csdn.net/code4101/article/details/2302052

幾個SQL語法(SqlServer)

base class ESS lse 簡單 table 可能 錯誤 mat 1,MERGE INTO 語句: 這個語法僅需要一次全表掃描就完成了全部工作,執行效率要高於INSERT+UPDATE,作用還是很強大的(簡單的說就是它可以批量更新和插入處理一個數據集,如果存在就更

Snapshot截圖軟件(一個小幾百k又的的截圖工具)

mar png 窗口 軟件 截屏 ffffff 編輯 tor 截圖 Snapshot截圖軟件(一個很小幾百k又很好用的的截圖工具)Snapshotor 是一款免費的截屏工具。軟件功能強大,支持截取窗口或一塊區域,也可以增加箭頭線,多邊形,文本等註釋。非常使用經常需要截圖並編

NSG2-一個的ns2的tcl指令碼自動生成軟體

NSG2-一個很好用的ns2的tcl指令碼自動生成軟體 來源:Linux社群 作者:fzxy002763 NSG2-一個很好用的ns2的tcl指令碼自動生成軟體,NSG2.rar,一個很好的java寫的tcl指令碼自動生成的軟體,在ns2上很實用可以直接通過畫圖畫拓撲,然後直接產生tc

一款的頁面滾動元素動畫外掛-AOS.JS

aos.js是一款效果超讚的頁面滾動元素動畫jQuery動畫庫外掛。該動畫庫可以在頁面滾動時提供28種不同的元素動畫效果,以及多種easing效果。在頁面往回滾動時,元素會恢復到原來的狀態。 載入方法: <link rel="stylesheet" href="/dist/aos.css" /

DateFormat自帶的日期轉換格式(的)

DateFormat自帶的一些日期轉化格式對於一些記不住yyyy.MM.dd等字母的同學 用自帶的完全可以解決一大部分的日期格式轉換 @Test public void run4(){ /** * Date轉化成 November 1, 20

【開源框架】Android 框架與UI效果demo收集,歡迎各位大俠補充

索引貼 持續更新 github開源專案收集貼 Android 初級到高階開發學習中的日常積累收集 目錄 UI效果與處理 Ui繫結註解 二維碼 Android動畫 Android圖表 文字輸入框 選擇列表