2013年2月4日月曜日

Bottleのレシピ


Bottleのレシピ

これは、コードスニペットと一般的なユースケースの例のコレクションです。

セッションの追跡

(マイクロフレームワークで)それをする正しい方法はありませんので、ありませんセッションのサポートを内蔵しています。 要件や環境によっては、フィッティングエンドとビーカーのミドルウェアを使用するか、自分でそれを実装することができます。ここでは、ファイルベースのバックエンドを持つビーカーセッションの例は、次のとおりです。
import bottle
from beaker.middleware import SessionMiddleware

session_opts = {
    'session.type': 'file',
    'session.cookie_expires': 300,
    'session.data_dir': './data',
    'session.auto': True
}
app = SessionMiddleware(bottle.app(), session_opts)

@bottle.route('/test')
def test():
  s = bottle.request.environ.get('beaker.session')
  s['test'] = s.get('test',0) + 1
  s.save()
  return 'Test counter: %d' % s['test']

bottle.run(app=app)

スタイルでのデバッグ:デバッグミドルウェア

ボトルは、クラッシュからWSGIサーバを防ぐために、あなたのアプリケーションコード内で発生したすべての例外をキャッチします。組み込みのdebug()モードでは十分ではありませんし、デバッグのミドルウェアに伝播する例外が必要な場合は、この動作をオフにすることができます。
import bottle
app = bottle.app()
app.catchall = False #Now most exceptions are re-raised within bottle.
myapp = DebuggingMiddleware(app) #Replace this with a middleware of your choice (see below)
bottle.run(app=myapp)
今、ボトルだけで独自の例外をキャッチ(HTTPError、HttpResponseとBottleException)とミドルウェアが残りを処理することができます。
非常に強力なデバッグWSGIミドルウェアとwerkzeug&ペーストライブラリが両方同梱されています。 ペーストのwerkzeugとpaste.evalexception.middleware.EvalExceptionためwerkzeug.debug.DebuggedApplicationを見てみましょう。 それら両方はスタックを検査を行うことができ、さらに、スタックのコンテキスト内でのpythonコードを実行するので 、生産にそれらを使用しないでください。

ユニット·テスト·ボトルアプリケーション

ユニット·テストは通常、WSGI環境を実行せずにWebアプリケーションで定義されているメソッドに対して実行されます。
単純な例では、Noseを使用して:
import bottle

@bottle.route('/')
def index():
    return 'Hi!'

if __name__ == '__main__':
    bottle.run()
Test script:
import mywebapp

def test_webapp_index():
    assert mywebapp.index() == 'Hi!'
例では、Bottleのroute()メソッドが実行されることはありません - index()はテストされています。

機能テストボトルアプリケーション

任意のHTTPベースのテストシステムは、実行中のWSGIサーバで使用されますが、いくつかのテストフレームワークがWSGIとより緊密に連携し、トレースバック機能とデバッグツールを駆使して制御された環境でコールWSGIアプリケーションを提供することができます。 WSGIのテストツールは良い出発点です。
from webtest import TestApp
import mywebapp

def test_functional_login_logout():
    app = TestApp(mywebapp.app)

    app.post('/login', {'user': 'foo', 'pass': 'bar'}) # log in and get a cookie

    assert app.get('/admin').status == '200 OK'        # fetch a page successfully

    app.get('/logout')                                 # log out
    app.reset()                                        # drop the cookie

    # fetch the same page, unsuccessfully
    assert app.get('/admin').status == '401 Unauthorized'

他のWSGIアプリを埋め込む

これは、おすすめの方法(あなたがこれを行うには、ボトルの前で、ミドルウェアを使用する必要があります)ではありませんが、あなたのボトルのアプリ内から他のWSGIアプリケーションを呼び出して、擬似ミドルウェアとしてボトルの行為をさせることができます。次に例を示します。
from bottle import request, response, route
subproject = SomeWSGIApplication()

@route('/subproject/:subpath#.*#', method='ALL')
def call_wsgi(subpath):
    new_environ = request.environ.copy()
    new_environ['SCRIPT_NAME'] = new_environ.get('SCRIPT_NAME','') + '/subproject'
    new_environ['PATH_INFO'] = '/' + subpath
    def start_response(status, headerlist):
        response.status = int(status.split()[0])
        for key, value in headerlist:
            response.add_header(key, value)
    return app(new_environ, start_response)
繰り返しますが、これはサブプロジェクトを実装するためのお勧めの方法ではありません。多くの人々は、このために要請し、WSGIにどのようにボトルの地図を表示するので、ここだけです。

末尾のスラッシュを無視する

ボトル/exampleと/example/ 2つの異なる経路である[1]。両方のURLを同じに扱うためには、2つの@ルートデコレータを追加することができます。
@route('/test')
@route('/test/')
def test(): return 'Slash? no?'
またはWSGIミドルウェアを追加してすべてのURLから末尾のスラッシュをストリップ。
class StripPathMiddleware(object):
  def __init__(self, app):
    self.app = app
  def __call__(self, e, h):
    e['PATH_INFO'] = e['PATH_INFO'].rstrip('/')
    return self.app(e,h)

app = bottle.app()
myapp = StripPathMiddleware(app)
bottle.run(app=myapp)
脚注 [1]それらはあるので。 <http://www.ietf.org/rfc/rfc3986.txt>を参照してください。

キープアライブ要求

注意

詳細な説明については、非同期アプリケーションへの入門を参照してください。
マルチXHRのようないくつかの"Push"メカニズムは、レスポンスヘッダと一緒に接続を閉じずに、応答データを書き込む機能が必要"Connection: keep-alive"。 WSGIは、この動作を容易に自分自身を貸すわけではありませんが、gevent非同期フレームワークを使用して、ボトルでそうすることも可能です。ここでgevent HTTPサーバまたはペーストHTTPサーバ(これは他の人と動作する可能性がありますが、私は試していません)のいずれかで動作するサンプルです。単にサーバーを変更するserver='gevent'とserver='paste'とユーザが貼り付けたサーバーを使用する:
from gevent import monkey; monkey.patch_all()

import time
from bottle import route, run

@route('/stream')
def stream():
    yield 'START'
    time.sleep(3)
    yield 'MIDDLE'
    time.sleep(5)
    yield 'END'

run(host='0.0.0.0', port=8080, server='gevent')
あなたがhttp://localhost:8080/streamを参照する場合、あなたは、"START" "MIDDLE"、と'END'の時間(むしろ一度にすべてを見るために8秒を待っているよりも)少なくとも一つを表示されるはずです。

ボトルのgzip圧縮

注意

詳細については、圧縮を参照してください。
要求時に静的リソース(CSSやJSファイルなど)を圧縮することで、サイトをスピードアップGzip圧縮をサポートするために、ボトルに共通の機能要求である。
Gzip圧縮をサポートするには、コーナー·ケースの数は頻繁にその作物アップのために単純な命題ではありません。適切なGzipでの実装を行う必要があります。
  • オンザフライで圧縮し、高速そうすることができます。
  • それをサポートしていないブラウザのために圧縮されません。
  • (画像、ビデオ)は、既に圧縮されているファイルを圧縮しないでください。
  • 動的なファイルを圧縮しないでください。
  • サポート2つ(GZIPおよびDeflate)圧縮アルゴリズムを異なっていた。
  • 頻繁に変更されませんキャッシュの圧縮ファイル。
  • いずれかのファイルが変更された場合はとにかくキャッシュを非検証します。
  • キャッシュが大きいに到達しないことを確認してください。
  • シークディスクがオンザフライ圧縮よりも長くかかるため、小さなファイルをキャッシュしません。
これらの要件から、それはgzip圧縮が最高のWSGIサーバのボトルの上で動作によって処理されているボトルプロジェクトの勧告です。そのようなCherryPyはなどのWSGIサーバはこれを達成するために使用することができGzipFilterミドルウェアを提供しています。

フックプラグインを使用して

あなたのURLのすべてによって返されたコンテンツのクロスオリジンリソースの共有を許可する場合たとえば、あなたはフックデコレータを使用してコールバック関数を設定することができます。
from bottle import hook, response, route

@hook('after_request')
def enable_cors():
    response.headers['Access-Control-Allow-Origin'] = '*'

@route('/foo')
def say_foo():
    return 'foo!'

@route('/bar')
def say_bar():
    return {'type': 'friendly', 'content': 'Hi!'}
また、すべての関数が呼び出される前にアクションを取ることbefore_callbackを使用することができます。

Herokuのとボトルを使用して

Herokuの、人気の高いクラウド·アプリケーション·プラットフォームは、今ではinfastructureでPythonアプリケーションを実行するためのサポートを提供しています。
このレシピは置き換えボトル固有のコードで、Herokuのクイックスタートに基づいていますHeroku/シーダーガイドのPython入門のあなたのアプリケーションのセクションを記述します。
import os
from bottle import route, run

@route("/")
def hello_world():
        return "Hello World!"

run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
Herokuのアプリのスタックは、アプリケーションがos.environ辞書を使用して、要求をlistenするために必要なポートを渡します。

Bottleの非同期アプリケーションへのプライマー


非同期アプリケーションへのプライマー

非同期デザインパターンは、WSGIの同期性とよく混在させないでください。ほとんどの非同期フレームワークは(tornado, twisted, ...)彼らの非同期機能を公開するために特殊なAPIを実装する理由はここにあります。ボトルはWSGIフレームワークはもともと備えてたWSGIとの同期性及び、素晴らしいgeventプロジェクトのおかげで、Bottleで非同期アプリケーションを作成することも可能です。この資料では、BottleのWSGIと非同期の使用を説明します。

同期WSGIの限界

簡単に言葉で表現すると、WSGI仕様(PEP 3333)は、要求/応答のサイクルを処理します。呼び出し可能なアプリケーションは各要求に対して1回ずつ呼び出され、ボディの反復子を返す必要があります。次に、サーバーは、ボディを反復処理し、ソケットに各チャンクを書き込みます。ボディの反復子が使い果たされるとすぐに、クライアント接続が閉じられます。
十分に単純ですが、思わぬ障害があります:これはすべて同期として発生します。アプリケーションがデータ(IO、ソケット、データベース、...)を待つ必要がある場合、それは空の文字列(ビジーウェイト)を得たり、現在のスレッドをブロックする必要があります。両方のソリューションは、処理スレッドを占有し、新しい要求に答えてからそれを防ぐことができます。その結果スレッドごとに1つだけ継続的な要求となります。
ほとんどのサーバーは、その比較的高いオーバーヘッドを回避するために、スレッドの数を制限します。 20以下のスレッドプールが共通しています。できるだけ早くすべてのスレッドが占有された場合は、任意の新しい接続が停止されています。サーバは他の皆のために効果的に終了させます。あなたはリアルタイムでアップデートを取得するためにロングポーリングAjaxリクエストを使用してチャットを実装する場合は、20の同時接続に制限された状態に到達すると思います。それはかなり小さいチャットです。

救助にGreenlets

ほとんどのサーバは切り替え処理において、新しいスレッドの作成にかかわる高いオーバーヘッドのために、同時実行スレッドの比較的低い数字にOSのワーキングプールのサイズを制限します。スレッドがプロセスをspawn(fork)に比べて安いですが、彼らはまだ新しい接続ごとに作成することが高価である。
geventモジュールを加え、greenletsを混在させます。 Greenletsは、従来のスレッドに似て動作しますが、作成することが非常にローコストです。 geventベースのサーバーは、ほとんどオーバーヘッドなしでgreenlets何千もの(接続ごとに1つ)を生成することができます。個々のgreenletsをブロックすると、新しい要求を受け入れるようにサーバの能力に影響を与えません。同時接続数は事実上無制限です。
彼らが見て、同期アプリケーションのように感じるので、これは驚くほど簡単に非同期アプリケーションを作成することができます。 geventベースのサーバーは、実際に非同期ですが、大規模なマルチスレッドではありません。次に例を示します。
from gevent import monkey; monkey.patch_all()

from time import sleep
from bottle import route, run

@route('/stream')
def stream():
    yield 'START'
    sleep(3)
    yield 'MIDDLE'
    sleep(5)
    yield 'END'

run(host='0.0.0.0', port=8080, server='gevent')
最初の行は重要です。それは、現在のスレッドをブロックしないように、PythonのブロッキングAPIのほとんどのmonkey-patchがgevent発生し、次のgreenletにCPUを渡す。それは実際にgeventベースの疑似スレッドでPythonのスレッドを置き換えます。あなたはまだ正常に全体のスレッドをブロックするtime.sleep()を使用することができます。あなたはmonkey-patchをpythonの組込みに快適に感じていない場合は、(この例ではgevent.sleep())に対応するgevent機能を使用することができます。
このスクリプトを実行してhttp://localhost:8080/streamをブラウザに表示した場合は、start、middle、およびEND1(むしろ一度にすべてを見るために8秒を待っているより)ずつを表示を表示する必要があります。それは通常のスレッドと全く同じに動作しますが、現在、サーバーは問題なく同時要求の数千を処理することができます。
注意

彼らはページのレンダリングを開始する前に、一部のブラウザでは、一定量のデータをバッファリングする。これらのブラウザで効果を確認するためにいくつかのバイト以上を得るために必要があるかもしれません。さらに、多くのブラウザはURLごとに同時接続の制限があります。このような場合は、2番目のブラウザやパフォーマンスを測定するベンチマーク·ツール(例えば、ABまたはhttperfの)を使用することができます。

イベントコールバック

非同期フレームワーク(including tornado, twisted, node.js and friends)で良く似た,共通のデザインパターンは非ブロッキングAPIと非同期イベントへのバインドのコールバックを使用することです。それはコールバックは後の時点で、ソケットへの書込みを許可する明示的にクローズされるまでのソケットオブジェクトが開いたままにしています。ここでtornadoライブラリに基づいて記述する例は、次のとおりです。
class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        worker = SomeAsyncWorker()
        worker.on_data(lambda chunk: self.write(chunk))
        worker.on_finish(lambda: self.finish())
主な利点は、要求ハンドラが早期に終了することです。コー​​ルバックは以前の要求のソケットへの書き込みを続行している間に処理スレッドが上に移動し、新しい要求を受け付けることができます。これは、これらのフレームワークはOSのスレッド数が少なく同時要求の多くを処理する管理方法です。
Gevent + WSGIと、処理は異なっています:まず、我々は新しい接続を受け入れるために(疑似)スレッドの無限のプールを持っているので、利点はありません早期終了します。それはソケットを(WSGIで必要)を閉じてしまうため第二に、我々は早期に終了することはできません。第三に、我々はWSGIに準拠するiterableを返さなければなりません。
WSGI標準に準拠するために、我々は非同期に書き込むことができるボディのiterableを返すことをしなければならない。 gevent.queueの助けを借りて、我々は切り離されたソケットをシミュレートし、次のように前の例を書き換えることができます。
@route('/fetch')
def fetch():
    body = gevent.queue.Queue()
    worker = SomeAsyncWorker()
    worker.on_data(lambda chunk: body.put(chunk))
    worker.on_finish(lambda: body.put(StopIteration))
    return body
サーバの観点から、キューオブジェクトは順次処理が可能です。それはブロックであれば、空と同時にそれがStopIterationに達すると停止します。これはWSGIに準拠しています。アプリケーション側では、キューオブジェクトが非ブロッキングソケットと同じように動作します。あなたは、いつでもそれに書き込み、それを周りに通過しさらにそれを非同期的に書き込みを行う新たな(疑似)スレッドを起動することができます。これは、ロングポーリングは時間のほとんどを実装する方法です。

最後に:WebSockets

しばらくの間、低レベルの詳細を忘れてWebSocketsについて話すことができます。ブラウザ(クライアント)とWebアプリケーション(サーバ)間の双方向通信チャネル:あなたがこの記事を読んでいるので、おそらくWebSocketsを理解されるでしょう。
ありがたいことに gevent-websocket packageは私たちの代わりに、すべてのハードワークを行います。ここでメッセージを受信し、ちょうどそれをクライアントに送り返す単純なはWebSocketエンドポイントは、次のとおりです。
from bottle import request, Bottle, abort
app = Bottle()

@app.route('/websocket')
def handle_websocket():
    wsock = request.environ.get('wsgi.websocket')
    if not wsock:
        abort(400, 'Expected WebSocket request.')

    while True:
        try:
            message = wsock.receive()
            wsock.send("Your message was: %r" % message)
        except WebSocketError:
            break

from gevent.pywsgi import WSGIServer
from geventwebsocket import WebSocketHandler, WebSocketError
server = WSGIServer(("0.0.0.0", 8080), app,
                    handler_class=WebSocketHandler)
server.serve_forever()
クライアントが接続を閉じるまで、whileループが実行されます。あなたのアイデア:)を得る
クライアントサイトのJavaScript APIも、まっすぐ実際に次のとおりです。
<!DOCTYPE html>
<html>
<head>
  <script type="text/javascript">
    var ws = new WebSocket("ws://example.com:8080/websocket");
    ws.onopen = function() {
        ws.send("Hello, world");
    };
    ws.onmessage = function (evt) {
        alert(evt.data);
    };
  </script>
</head>
</html>

Bottleのチュートリアル:To-Do - リスト·アプリケーション



チュートリアル:To-Do - リスト·アプリケーション

注意
このチュートリアルでは、progessの作品であるとnoisefloorによって書かれた。
このチュートリアルでは、Bottle-WSGIフレームワークについて、簡単に紹介をする必要があります。このチュートリアルでは、ボトルWSGIフレームワークを簡単に紹介を与える必要があります。主な目標は、ボトルを使用してプロジェクトを作成するには、このチュートリアルを読んだ後、できるようにすることです。 このドキュメント内で、すべての機能は、説明されますが、少なくともBottleのテンプレート能力を利用したフォーマット出力の取扱いと、主要なルーティングとGET/POSTパラメータ。
ここで内容を理解するために、WSGIの基本的な知識を持っている必要はありません。Bollteは、とにかくユーザーからWSGIを遠ざけるしようとしています。あなたは、Pythonプログラミング言語を正しく理解しておく必要があります。 また、チュートリアルの取得およびSQL databseにデータを格納に使用される例ですので、SQLに関する基本的な考え方は役立ちますが、Bollteの概念を理解する為には必要ありません。ここで、SQLiteは使用されています。 ブラウザに送信されるBollteの出力は、HTMLの助けを借りていくつかの例でフォーマットされています。 したがって、一般的なHTMLタグについての基本的な考え方は同様に役立ちます。
Bollteを導入するために、"in between" Pythonのコードは、フォーカスを維持するために、短くされています。 また、チュートリアル内のすべてのコードは、正常に動作しています. しかし、あなたは必ずしも例えば、 "in the wild" それを使用することはできません 公共のWebサーバー上の。 そのためには、あなたは、例えばを追加することができ,より多くのエラー処理、パスワードを使用してデータベースを保護するP-:

目標

このチュートリアルの最後に、我々は、シンプルなWebベースのToDoリストがあります。リストには、各項目のテキスト(最大100文字)と状態(閉じた場合は0、開いている1)が含まれています。 Webベースのユーザーインターフェイスを介して、開いているアイテムは、ビューすることができ、編集し、新しいアイテムを追加することができます。
開発中に、すべてのページはlocalhost上で利用できるようになりますが、後でその上には、Apacheのmod_wsgiを一緒に使用する方法など、"本物の"サーバ用のアプリケーションに適応する方法を表示されます。
Bollteには、テンプレートの助けを借りて、ルーティングとフォーマット出力を行います。リストの項目は、SQLiteデータベース内に格納されます。データベースの読み取りと書き込みはPythonコードによって行われます。

始める前に…

ボトルをインストールする

あなたがPython(バージョン2.5以上)の比較的新しいインストールされていると仮定して、あなただけのそれに加えてボトルをインストールする必要があります。ボトルは、Python自体よりも他の依存関係を持っていません。
手動でボトルをインストールするか、Pythonのeasy_installを使用することができ、次のいずれかeasy_installを

ボトルを

さらにソ​​フトウェアの必需品
我々はデータベースとしてSQLite3を使うとして、それがインストールされていることを確認してください。 Linuxシステムでは、ほとんどのディストリビューションではsqlite3デフォルトでインストールされています。 SQLiteは、同様にWindowsとMacOS Xのために利用可能で、sqlite3モジュールは、Python標準ライブラリの一部です。

SQLデータベースを作成します。

最初に、我々は後で使用するデータベースを作成する必要があります。これを行うには、プロジェクトディレクトリに次のスクリプトを保存およびPythonを使用して、それを実行します。あなたも、対話型インタプリタを使用することができます。
import sqlite3
con = sqlite3.connect('todo.db') # Warning: This file is created in the current directory
con.execute("CREATE TABLE todo (id INTEGER PRIMARY KEY, task char(100) NOT NULL, status bool NOT NULL)")
con.execute("INSERT INTO todo (task,status) VALUES ('Read A-byte-of-python to get a good introduction into Python',0)")
con.execute("INSERT INTO todo (task,status) VALUES ('Visit the Python website',1)")
con.execute("INSERT INTO todo (task,status) VALUES ('Test various editors for and check the syntax highlighting',1)")
con.execute("INSERT INTO todo (task,status) VALUES ('Choose your favorite WSGI-Framework',0)")
con.commit()
これは、データベースファイルのTODOと呼ばれるテーブルと3つの列idを持つtodo.db、タスク、およびステータスを生成します。 idは、行を参照するために後で使用される行ごとに、一意のIDです。列タスクは、タスクを説明するテキストを保持し、それは最大100文字の長さにすることができます。最後に、列のステータスはopen(値1)かクローズ(値0)としてタスクをマークするために使用されています。

Bottleを使用したWebベースのToDoリスト

今ではWebベースのアプリケーションを作成するために、Bottleを導入する必要がある。 routes:しかし、最初に、我々は、ボトルの基本的な概念に注目する必要があります。

routesを理解する

ページアドレスが呼び出されたときに、基本的には、ブラウザに表示され、各ページが動的に生成されます。したがって、静的なコンテンツはありません。サーバー上の特定のアドレス:これは、ボトル内の "routes"と呼ばれるものとまったく同じです。 "TODO"のルートに定義されている(Python)関数がある場合は、したがって、たとえば、ページhttp://localhost:8080/todoがブラウザから呼び出されたときに、Bottleには、コールとのチェックを “grabs” 。そうだとすれば、ボトルには、対応するPythonコードを実行し、その結果を返します。

最初のステップ - すべてのアイテムを表示

したがって、ルートの概念を理解した後に、まず1を作成することができます。目標は、ToDoリストから全ての項目を開いて表示する事です。
import sqlite3
from bottle import route, run

@route('/todo')
def todo_list():
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT id, task FROM todo WHERE status LIKE '1'")
    result = c.fetchall()
    return str(result)

run()
ファイルと同じディレクトリに、好ましくは、コードのtodo.pyを保存todo.db. そうでなければ、sqlite3.connect()ステートメントでtodo.dbへのパスを追加する必要があります。
我々だけで何をしたかを見てみましょう: 私たちは、SQLiteデータベースにアクセスするためにsqlite3の必要なモジュールをインポートし、 Bottleから我々はroutesとrunをインポートしました。run()文は、単にBottleに含まれるWebサーバーを起動します。デフォルトでは、Webサーバーは、localhostのポート8080上のページを提供します。さらに、我々はBottleのルーティングの責任関数であるルートをインポートします。あなたが見ることができるように、我々は、データベースからの読み込み、数行のコードで、一つの関数todo_list()を定義しました。重要な点は、右のdef todo_list()ステートメントの前に、デコレータ文@route('/todo')です。これにより、我々はroot/todoには、この関数をバインドするので、毎回ブラウザがhttp://localhost:8080/todoを呼び出して、Bottleは、関数todo_list()の結果を返します。それは瓶の作品内のルーティング方法です。
実際には、関数に複数のルートにバインドすることができます。次のコードは、だから:
@route('/todo')
@route('/my_todo_list')
def todo_list():
    ...
あまりにも、正常に動作します。何が動作しませんと、1つ以上の関数に1つのルートをバインドすることです。
どのブラウザで表示されますと、このように、返されるreturn文で指定された値です。この例では、Bottleにはreturn文から文字列または文字列のリストを期待するようには、str()によって文字列に結果を変換する必要があります。しかし、ここでは、データベースクエリの結果は、Python DB APIで定義されている標準であるタプルのリストです。
今、少し上のスクリプトを理解した上で、それを実行し、結果自分自身を監視する時間です。 Linuxベース/Unixベースのシステム上のファイルtodo.pyを最初に実行する必要があることを覚えておいてください。その後、ちょうどのpython todo.pyを実行し、ブラウザでページhttp://localhost:8080/todoを呼び出します。スクリプトを書き込んでもミスを犯しません場合、出力は次のようになります
[(2, u'Visit the Python website'), (3, u'Test various editors for and check the syntax highlighting')]
もし、上記の表示なら - おめでとうございます!あなたは、ボトルの成功したユーザーです。もし、動作しなかった場合は、スクリプトにいくつかの変更を加える必要があります、その時にページを提供しているボトルを停止することを忘れないでください。別の方法としての改訂版がロードは提供されません。 実際に、それは、SQLクエリから返される生の結果ですので、出力結果を読むことは本当にエキサイティングでも良いものでもありません。そこで、次の手順で私たちはよりよい方法で出力をフォーマットします。しかし我々はそれを行う前に、私たちは私たちの作業がが容易になる方法があります。

デバッグとオートリロード

たぶん、あなたはすでにスクリプトが間違って、例えば、Bollteの中に、何かのブラウザに短いエラーメッセージを送信することに気づいたり、データベースへの接続が動作していなかったり。 デバッグの目的のために、それはより多くの細かい情報を得るために非常に便利は方法があります。 これは簡単にスクリプトに次のステートメントを追加することによって達成することができます:
from bottle import run, route, debug
...
#add this at the very end:
debug(True)
run()
"debug"を有効にすることで、通常のバグを見つけるための有用な情報が含まれているPythonインタプリタの完全なスタックトレースを取得します。さらに、テンプレートは(下記参照)、キャッシュされませんので、テンプレートへの変更は、サーバーを停止せずに有効になります。
警告
(真)のみの開発に使用されることになって、デバッグは、実稼働環境で使用すべきではありません。
さらに静かな便利な機能)は、ステートメントの実行を(変更することによって有効になっている自動リロードで、
run(reloader=True)
これは自動的にスクリプトへの変更を検出し、それが再び呼び出された後、サーバーを停止および起動することなく、新しいバージョンをリロードします。
再び、この機能は、主に開発中ではなく、生産性の高いシステムで使用されることになっています。

出力をフォーマットするボトルテンプレート

今度は、適切な形式にスクリプトの出力をキャストを見てみましょう。
実際にボトルには、文字列または関数から文字列のリストを受け取ることを想定し、ブラウザへの組み込みサーバの助けによってそれらを返します。ボトルは、文字列自体の内容については気にしないので、テキストも、HTMLマークアップを使用してフォーマットすることができます。
ボトルには、それに自身の使いやすいテンプレートエンジンをもたらします。テンプレートは、TPL拡張子を持つ別々のファイルとして保存されます。テンプレートは、関数内から次に呼び出すことができます。テンプレートは、テキストの任意の型を(ほとんどのHTMLマークアップPython文と混合される)を含めることができます。さらに、テンプレートは、例えば、引数を取ることができます結果は、テンプレート内にきれいにフォーマットされるデータベースクエリのセット。
右ここで、我々は2つ​​の列を持つ単純なテーブルに開いているのToDo項目を表示し、クエリの結果をキャストしようとしている最初の列は、番目の列のテキスト、アイテムのIDが含まれています。結果セットは、上で見たように、タプルのリストで、各タプルは、結果の1セットが含まれています。
この例ではテンプレートを含めるには、単に次の行を追加します。
from bottle import route, run, debug, template
...
result = c.fetchall()
c.close()
output = template('make_table', rows=result)
return output
...
だから我々はここで2つのことを行う: まず、我々は、テンプレートを使用できるようにするためにボトルからテンプレートをインポートします。第二に、我々は、戻された変数の出力へのmake_tableテンプレートの出力を割り当てることができます。テンプレートを呼び出すことに加えて、我々は、後にテンプレート内で使用される変数の行に、我々は、データベースクエリから受け取った結果を割り当てます。必要であれば、テンプレートに複数の変数/値を割り当てることができます。
テンプレートは常に文字列のリストを返すので、何かを変換する必要はありません。もちろん、我々は正確に上記と同じ結果が得られるリターンテンプレート( 'make_table'、行=結果)を書き込むことによって、コードの1行を保存することができます。
今ではこのようになり、対応するテンプレートを書くための時間です。
%#template to generate a HTML table from a list of tuples (or list of lists, or tuple of tuples or ...)
<p>The open items are as follows:</p>
<table border="1">
%for row in rows:
  <tr>
  %for col in row:
    <td>{{col}}</td>
  %end
  </tr>
%end
</table>
todo.pyが格納されている同じディレクトリにmake_table.tplとしてコードを保存します。
それでは、コードを見てみましょう:%で始まるすべての行は、Pythonコードとして解釈されます。もちろん、唯一の有効なPythonのステートメントが許可され、それ以外のテンプレートは他のPythonコードとして、例外が発生しますのでご注意ください。他の行はプレーンなHTMLマークアップがあります。
あなたが見ることができるように、我々は、行を通過するためには、for文を2回Pythonのを使用しています。このように、行がデータベースクエリの結果を保持する変数ですので、タプルのリストです。文の最初にリスト内のタプルは、テーブルのセルにそれぞれ配置されたタプル内の第1項目にアクセスします。 %終了したなどの文間に、それ以外の出力はあなたが期待するものではないかもしれない、場合は、あなたがすべてを閉じておくことが重要です。
テンプレート内の非Pythonのコードライン内の変数にアクセスする必要がある場合は、二重括弧に入れてする必要があります。これは、代わりに変数、右の実際の値を挿入するテンプレートを示します。
スクリプトを再度実行し、出力を見てください。それでも本当に素敵でなく、タプルのリストよりも少なくとも読みやすくなります。もちろん、あなたが上記のスパイスアップは非常に単純なHTMLマークアップは、例えば缶見栄えの良い出力を得るためにインラインスタイルを使用します。

GETとPOSTの値を使用して、

我々は適切に開いているすべての項目を確認することができますように、我々はToDoリストに新しい項目を追加して次のステップに移動します。新しいアイテムは、GETメソッドでそのデータを送信し、通常のHTMLベースのフォームから受信する必要があります。
これを行うには、まず私たちのスクリプトに新しいルートを追加し、それがGETデータを取得しなければならないルートを教えて:
from bottle import route, run, debug, template, request
...
return template('make_table', rows=result)
...

@route('/new', method='GET')
def new_item():

    new = request.GET.get('task', '').strip()

    conn = sqlite3.connect('todo.db')
    c = conn.cursor()

    c.execute("INSERT INTO todo (task,status) VALUES (?,?)", (new,1))
    new_id = c.lastrowid

    conn.commit()
    c.close()

    return '<p>The new task was inserted into the database, the ID is %s</p>' % new_id
GET(またはPOST)データにアクセスするには、我々は、ボトルからの要求をインポートする必要があります。変数に実際のデータを割り当てるには、我々は、request.GET.get('task',).strip() を使用します。タスクは、我々がアクセスするGETデータの名前であるストリップ()ステートメントを、。それがすべてです。あなたのGETデータ(複数の変数は、複数のrequest.GET.getを持っている場合)ステートメントを使用し、他の変数に割り当てることができます。
コー​​ドのこの部分の残りの部分はちょうど得られたデータの処理は次のとおりです。データベースへの書き込み、データベースから対応するIDを取得し、出力を生成します。
しかし、我々はどこからGETデータを取得するのですか?まあ、我々は、フォームを保持する静的なHTMLページを使用することができます。または、我々は今何をすべきか、root/newがGETデータなしで呼び出されると、出力されるテンプレートを使用することです。
コー​​ドでは、拡張する必要があります。
...
@route('/new', method='GET')
def new_item():

if request.GET.get('save','').strip():

    new = request.GET.get('task', '').strip()
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()

    c.execute("INSERT INTO todo (task,status) VALUES (?,?)", (new,1))
    new_id = c.lastrowid

    conn.commit()
    c.close()

    return '<p>The new task was inserted into the database, the ID is %s</p>' % new_id
else:
    return template('new_task.tpl')
new_task.tplは次のようになります。
<p>Add a new task to the ToDo list:</p>
<form action="/new" method="GET">
<input type="text" size="100" maxlength="100" name="task">
<input type="submit" name="save" value="save">
</form>
それがすべてです。あなたが見ることができるように、テンプレートはプレーン今回はHTMLです。
今、我々は行うにはリストを拡張することができます。
ところで、POSTデータを使用したい場合:これは、まったく同じように動作しますが、だけではなく、request.POST.get()を使用します。

既存のアイテムの編集

行うための最後のポイントは、既存のアイテムの編集を有効にすることです。
ルートだけを使用することによって我々はそれが可能である今のところ知っているが、かなりトリッキーかもしれません。しかし、ボトルは、このタスクは非常に簡単に "ダイナミックルート"と呼ばれる何かを知っている。
ダイナミックルートの基本的なステートメントは次のようになります。
@route('/myroute/:something')
ここでキーポイントはコロンです。これがために受け入れるようにボトルに指示します。次のスラッシュまで何か任意の文字列を。また、何かの値が、そのルートに割り当てられた関数に渡されるので、データが関数内で処理することができます。
無編集するアイテムのIDです:私たちのToDoリストについては、我々はルート@ルート(無 '/ /編集)を作成します。
コー​​ドは次のようになります。
@route('/edit/:no', method='GET')
def edit_item(no):

    if request.GET.get('save','').strip():
        edit = request.GET.get('task','').strip()
        status = request.GET.get('status','').strip()

        if status == 'open':
            status = 1
        else:
            status = 0

        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        c.execute("UPDATE todo SET task = ?, status = ? WHERE id LIKE ?", (edit, status, no))
        conn.commit()

        return '<p>The item number %s was successfully updated</p>' % no
    else:
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        c.execute("SELECT task FROM todo WHERE id LIKE ?", (str(no)))
        cur_data = c.fetchone()

        return template('edit_task', old=cur_data, no=no))
いや、ここで対応する関数の番号を渡します。これはかなり基本的に我々はすでにここでの主な追加が動的経路を使用しているなどのデータをGETを使用して同じように、新しい項目を追加する際に、上記したものと同じです。あなたが見ることができるように、NOは、データベース内のデータの右側の行にアクセスする関数内で使用されていません。
関数内で呼ばれるテンプレートedit_task.tplは次のようになります。
%#template for editing a task
%#the template expects to receive a value for "no" as well a "old", the text of the selected ToDo item
<p>Edit the task with ID = {{no}}</p>
<form action="/edit/{{no}}" method="get">
<input type="text" name="task" value="{{old[0]}}" size="100" maxlength="100">
<select name="status">
<option>open</option>
<option>closed</option>
</select>
<br/>
<input type="submit" name="save" value="save">
</form>
既に上述したように再び、このテンプレートは、Pythonの文とHTMLが混在しています。
ダイナミックルートの最後の単語:後で示すようにあなたは、動的経路の正規表現を使用していてもすることができます。

ダイナミックルートの検証

ダイナミックルートを使用すると良いですが、多くのケースでそれはルートの動的な部分を検証するために理にかなっています。たとえば、我々は上記の編集のために私たちのルートの整数を期待しています。しかし、浮動小数点の場合、その文字が受信され、Pythonインタプリタは、我々が望むものではありません例外をスローします。
そのような場合には、ボトルの前に関数に渡すに "入力"を検証する@検証デコレータを提供しています。バリデータを適用するためには、次のようにコードを拡張します。
from bottle import route, run, debug, template, request, validate
...
@route('/edit/:no', method='GET')
@validate(no=int)
def edit_item(no):
...
最初に、我々は@検証 - デコレータを適用するよりも、ボトルのフレームワークから検証するインポートされます。何が整数の場合、右ここで、我々は検証しません。基本的には、検証がフロートのようなすべての種類のデータで動作し、リストなど
コー​​ドを保存し、ために "403"の値を使用して再度ページを呼び出す:いいえ、例えば、フロート。整数が期待されていたという、エラー - あなたがいない例外が、 "紫禁城403"が表示されます。
正規表現を使用した動的経路
ボトルもルートの "動的な部分が"正規表現することができますダイナミックルートを扱うことができます。
そう、ちょうど、私たちのToDoリスト内のすべての単一の項目は、例えばのような用語で、それらのプレーン番号でアクセス可能であるべきであると仮定していることを実証する"項目1"。明白な理由については、各項目のルートを作成する必要はありません。さらに、単純なダイナミックルートは、ルートの一部として、用語 "アイテム"は静的であり、いずれかの動作しません。
上記に述べたように、解決策は、正規表現です。
@route('/item:item#[1-9]+#')
def show_item(item):
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT task FROM todo WHERE id LIKE ?", (item))
    result = c.fetchall()
    c.close()
    if not result:
        return 'This item number does not exist!'
    else:
        return 'Task: %s' %result[0]
もちろん、この例では、何らかの形で人為的に構築される - それが唯一の検証と組み合わせて単純な動的経路を使用する方が簡単でしょう。それにもかかわらず、我々は、正規表現のルートの作業方法を見たい:行@ルート(/アイテム:item_#[1-9] +#)通常のルートと同様に起動しますが、#で囲まれた部分が正規表現として解釈され、どのルートの動的な部分です。したがって、この場合には、我々は、0〜9の間の任意の数字を一致させる必要があります。次の関数 "show_item"は単に与えられた項目がデータベースかどうかに存在するかどうかを確認します。それが存在する場合には、タスクの対応するテキストが返されます。あなたが見ることができるように、ルートの唯一の正規表現の部分は前方に渡されます。さらに、それは常に、それがこのケースのように、単純な整数である場合でも、文字列として転送されます。

静的ファイルを返す

時にはそれがないPythonの関数へのルートを関連付けることが必要になるかもしれませんが、単に静的なファイルを返します。あなたは、たとえばアプリケーションのヘルプ·ページを持っているなら、あなたは普通のHTMLとしてこのページを返すようにすることができます。これは次のように動作します。
from bottle import route, run, debug, template, request, validate, static_file

@route('/help')
def help():
    return static_file('help.html', root='/path/to/file')
まず、ボトルからstatic_file関数をインポートする必要があります。あなたが見ることができるように、戻りstatic_fileステートメントは、returnステートメントを置き換えます。返されるファイルの名前とファイルへのパス:これは、少なくとも2つの引数を取ります。ファイルがアプリケーションと同じディレクトリにある場合でも、パスは記載する必要があります。しかし、この場合には、使用することができます '。'パスとして、あまりにも。ボトルを推測し、自動的にファイルのMIMEタイプが、場合には、ここでMIMEタイプ= "テキスト/html'であろうstatic_file、3番目の引数を追加し、明示的に述べたい。 static_fileは、動的なものも含めて、ルートの任意のタイプで動作します。

JSONデータを返す

あなたのアプリケーションに直接出力を生成したいのですが、データがでさらに処理されるように返されない場合、例えば、あるかもしれませんJavaScriptによる。そのような場合には、ボトルには、Webアプリケーション間でデータを交換するための標準の一種であるJSONオブジェクトを返すように可能性を提供しています。さらに、JSONはPythonを含め、多くのプログラミング言語で処理することができ
だから、Let 'sは、我々はJSONオブジェクトとして正規表現の経路の例で生成されたデータを返すようにしたいと仮定します。コー​​ドは次のようになります。
@route('/json:json#[1-9]+#')
def show_json(json):
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT task FROM todo WHERE id LIKE ?", (json))
    result = c.fetchall()
    c.close()

    if not result:
        return {'task':'This item number does not exist!'}
    else:
        return {'Task': result[0]}
することができますように、その非常に簡単です:単に通常のPython辞書とボトルを返すと、送信する前にJSONオブジェクトに自動的に変換されます。ですから、例えばもし"http://localhost/json1"ボトルと呼ぶこの場合にはJSONオブジェクトを返す必要があります{"タスク":["Pythonに優れた入門書を取得するために、バイトの-pythonを読む"]}。

エラーのキャッチ

次のステップは、アプリケーションのユーザーからのエラーメッセージのいずれかのタイプを遠ざけるために、ボトル自体にエラーをキャッチすることであるかもしれません。これを行うには、ボトルは、HTMLエラーに割り当てることができます "というエラー·ルート"を持っています。
私たちのケースでは、403エラーをキャッチしたいと思います。次のようにコードは次のとおりです。
from bottle import error

@error(403)
def mistake(code):
    return 'The parameter you passed has the wrong format!'
だから、最初に我々は、ボトルからエラーをインポートし、すべての "403 Forbidden"エラーをキャッチし、エラー(403)によって経路を定義する必要があります。関数は "間違い"がそれに割り当てられています。あなたがそれを必要としない場合でも、 - エラー()は常に関数にエラーコードを渡すことに注意してください。したがって、関数は常に一つの引数を受け取る必要があり、それ以外の場合は動作しません。
再び、関数に複数のエラーのルートを割り当てる、または1つの機能それぞれにさまざまなエラーをキャッチすることができます。このコードのように:
@error(404)
@error(403)
def mistake(code):
    return 'There is something wrong!'
次の1つは同様に、正常に動作します。
@error(403)
def mistake403(code):
    return 'The parameter you passed has the wrong format!'

@error(404)
def mistake404(code):
    return 'Sorry, this page does not exist!'

要約

上記のすべてのセクションを通過した後は、ボトルWSGIフレームワークがどのように動作する簡単な理解しておく必要があります。さらに、あなたのアプリケーションのためにボトルを使用するために必要なすべての知識を持っています。
次の章では、大規模なプロジェクトのためにボトルに適応する方法を簡単に紹介を提供します。さらに、我々はこれまでに使用されるものよりも高い負荷/より多くのWebトラフィックのパフォーマンスが向上し、Webサーバーとボトルを操作する方法を示します。

サーバーのセットアップ

これまでのところ、我々は、Pythonに付属のWSGIリファレンスサーバーであるボトルで使用される標準的なサーバを使用していました。このサーバは、開発目的のために完璧に適していますが、それは大規模なアプリケーションのために本当に適していません。我々は選択肢を見ている前に、のは、まず標準的なサーバーの設定を微調整する方法を見てみましょう。

別のポートとIPアドレスにボトルを実行する

標準として、ボトルもlocalhostとして知られているIPアドレス127.0.0.1上、ポート8080でページを提供します。追加のパラメータは、ポートとアドレスを変更するには、ボトルのrun()関数に渡すことができるように設定を変更するには、非常に単純です。
ポートを変更するには、runコマンドにポート=ポート番号を追加します。したがって、たとえば:
run(port=80)
ボトルは、ポート80をリッスンするでしょう。
:ボトルがリッスンしているIPアドレスを変更するには
run(host='123.45.67.89')
もちろん、両方のパラメータが同じように、組み合わせることができます。
run(port=80, host='123.45.67.89')
ボトルは別のサーバーで実行されているときに、ポートおよびホストパラメータは、次のセクションに示すように、適用することができます。

別のサーバーにボトルを実行する

上記に述べたように、標準のサーバ、開発、個人的な使用のみボトルに基づいてアプリケーションを使用している人々の小さなグループのために完璧に適しています。それはシングルスレッドであるとして大規模なタスクのために、標準のサーバーは、ボトルネックになる可能性があり、したがって、それだけで一度に1つの要求を提供することができます。
しかし、ボトルは、すでに高い負荷でパフォーマンスが向上し、ボード上のマルチスレッド·サーバーへの各種アダプタを持っています。ボトルは、CherryPyをFapws3、flupのと貼り付けをサポートしています。
[貼り付け]サーバとの例では、ボトルのために実行する場合は、次のコードを使用します。
from bottle import PasteServer
...
run(server=PasteServer)
これはFlupServer、CherryPyServerとFapwsServerとまったく同じように動作します。

mod_wsgiのApacheとでボトルを実行する

多分あなたは既にApacheがありますか、ボトルベースの​​アプリケーションを大規模に実行したい - それはmod_wsgiでApacheを考える時間です。
私達はあなたのApacheサーバーが稼働中で、mod_wsgiをも同様に正常に動作していることを前提としています。 Linuxディストリビューションの多くに、mod_wsgiのが容易に使用されているどのようなパッケージ管理システムを介してインストールすることができます。
ボトルはそれとmod_wsgiのためのアダプタをもたらすので、あなたのアプリケーションを提供するのは簡単な作業です。
次の例では、我々は、あなたのアプリケーションが "ToDoリスト"を作成することを前提としていhttp://www.mypage.com/todo、コード、テンプレート、SQLiteデータベースを介してアクセスがパスに格納されているの/ var / www / TODO 。
あなたがmod_wsgiを介してアプリケーションを実行すると、それはrun()あなたのコードからステートメントを削除することが不可欠です、それ以外の場合は、ここでは動作しません。
その後、次の内容のadapter.wsgiというファイルを作成します。
import sys, os, bottle

sys.path = ['/var/www/todo/'] + sys.path
os.chdir(os.path.dirname(__file__))

import todo # This loads your application

application = bottle.default_app()
との/ var / www / TODO、同じパスに保存してください。実際にファイルの名前は、長い拡張子があるので、何もすることができます。WSGI。名前は、仮想ホストからファイルを参照するために使用されています。
最後に、我々は次のようになります。Apacheの設定に仮想ホストを追加する必要があります。
<VirtualHost *>
    ServerName mypage.com

    WSGIDaemonProcess todo user=www-data group=www-data processes=1 threads=5
    WSGIScriptAlias / /var/www/todo/adapter.wsgi

    <Directory /var/www/todo>
        WSGIProcessGroup todo
        WSGIApplicationGroup %{GLOBAL}
        Order deny,allow
        Allow from all
    </Directory>
</VirtualHost>
サーバを再起動した後、あなたのToDoリストには、http://www.mypage.com/todoでアクセス可能でなければなりません

最後の言葉

今、私たちはボトルには、この導入とチュートリアルの最後にあります。我々は、ボトルの基本的な概念について学んだとボトルのフレームワークを使用した最初のアプリケーションを作成しました。それに加えて、我々は、大規模なタスクのためのボトルを適応し、mod_wsgiのでApacheウェブサーバを介してボトルを提供する方法を説明しました。
導入で述べたように、このチュートリアルでは、ボトルのすべての色合いと可能性を示していません。私たちはここではスキップすること例えばですファイルオブジェクトとストリームを受信する方法と、認証データを処理する。さらに、テンプレートを別のテンプレート内から呼び出すことができますどのように表示されませんでした。これらの点への導入については、フルボトルのマニュアルを参照してください。 完全な例リスト
ToDoリストの例は、ピース単位で開発されたとして、ここでは、完全なリストは、次のとおりです。
アプリケーションtodo.pyのメインのコード:
import sqlite3
from bottle import route, run, debug, template, request, validate, static_file, error

# only needed when you run Bottle on mod_wsgi
from bottle import default_app

@route('/todo')
def todo_list():

    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT id, task FROM todo WHERE status LIKE '1';")
    result = c.fetchall()
    c.close()

    output = template('make_table', rows=result)
    return output

@route('/new', method='GET')
def new_item():

    if request.GET.get('save','').strip():

        new = request.GET.get('task', '').strip()
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()

        c.execute("INSERT INTO todo (task,status) VALUES (?,?)", (new,1))
        new_id = c.lastrowid

        conn.commit()
        c.close()

        return '<p>The new task was inserted into the database, the ID is %s</p>' % new_id

    else:
        return template('new_task.tpl')

@route('/edit/:no', method='GET')
@validate(no=int)
def edit_item(no):

    if request.GET.get('save','').strip():
        edit = request.GET.get('task','').strip()
        status = request.GET.get('status','').strip()

        if status == 'open':
            status = 1
        else:
            status = 0

        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        c.execute("UPDATE todo SET task = ?, status = ? WHERE id LIKE ?", (edit,status,no))
        conn.commit()

        return '<p>The item number %s was successfully updated</p>' %no

    else:
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        c.execute("SELECT task FROM todo WHERE id LIKE ?", (str(no)))
        cur_data = c.fetchone()

        return template('edit_task', old = cur_data, no = no)

@route('/item:item#[1-9]+#')
def show_item(item):

        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        c.execute("SELECT task FROM todo WHERE id LIKE ?", (item))
        result = c.fetchall()
        c.close()

        if not result:
            return 'This item number does not exist!'
        else:
            return 'Task: %s' %result[0]

@route('/help')
def help():

    static_file('help.html', root='.')

@route('/json:json#[1-9]+#')
def show_json(json):

    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT task FROM todo WHERE id LIKE ?", (json))
    result = c.fetchall()
    c.close()

    if not result:
        return {'task':'This item number does not exist!'}
    else:
        return {'Task': result[0]}


@error(403)
def mistake403(code):
    return 'There is a mistake in your url!'

@error(404)
def mistake404(code):
    return 'Sorry, this page does not exist!'


debug(True)
run(reloader=True)
#remember to remove reloader=True and debug(True) when you move your application from development to a productive environment
テンプレートmake_table.tpl:
%#template to generate a HTML table from a list of tuples (or list of lists, or tuple of tuples or ...)
<p>The open items are as follows:</p>
<table border="1">
%for row in rows:
  <tr>
  %for col in row:
    <td>{{col}}</td>
  %end
  </tr>
%end
</table>
テンプレートedit_task.tpl:
%#template for editing a task
%#the template expects to receive a value for "no" as well a "old", the text of the selected ToDo item
<p>Edit the task with ID = {{no}}</p>
<form action="/edit/{{no}}" method="get">
<input type="text" name="task" value="{{old[0]}}" size="100" maxlength="100">
<select name="status">
<option>open</option>
<option>closed</option>
</select>
<br/>
<input type="submit" name="save" value="save">
</form>
Template new_task.tpl:
%#template for the form for a new task
<p>Add a new task to the ToDo list:</p>
<form action="/new" method="GET">
<input type="text" size="100" maxlength="100" name="task">
<input type="submit" name="save" value="save">
</form>

2013年2月3日日曜日

Bottle Tutorial

Bottle: Python Web Framework

CentOSでCUIのメール環境を構築する


CentOSでCUIのメール環境を構築する

ここでは、下記にミドルウエアの環境構築を行います。
1. Heirloom mailx
2. postfix

Heirloom mailx

1. 概要

Heirloom mailxとは、
/bin/mailの拡張版ともいえる、「Heirloom mailx」をインストールします。 「Heirloom mailx」は、IMAP、POP3、APOP、SSL/TLS、S/MIMEに対応したLinux版のCUIメーラです。

2. インストール

以下のコマンドでインストールを行います
$ cd /usr/local/src
$ wget http://sourceforge.net/projects/heirloom/files/latest/download
$ tar -jxf mailx-12.4.tar.bz2
$ cd mailx-12.4
# make
# make install UCBINSTALL=/usr/bin/install

3. 環境設定

利用するユーザ単位で、.mailrcを作成します ここでは、Gmailのメールサーバを利用することにします
$ vim ~/.mailrc
# .mailrc Gmail config
account gmail {
   set imap-cache=~/MailDir
   set imap-use-starttls
   set folder=imaps://Gmailのユーザ名@gmail.com@imap.gmail.com
   set password-Gmailのユーザ名@gmail.com@imap.gmail.com=Gmailのパスワード 
   set from=”お名前 <Gmailのユーザ名@gmail.com>”
   set record=+Sent
   set smtp-use-starttls
   set smtp=smtp://smtp.gmail.com:587
   set smtp-auth=login
   set smtp-auth-user=Gmailのユーザ名@gmail.com
   set smtp-auth-password=Gmailのパスワード
   set nss-config-dir=~/.mozilla/firefox/xxxxxxx.xxxx プロファイル情報
   shortcut allmail +[Gmail]/All\ Mail
   shortcut spam +[Gmail]/Spam
   shortcut trash +[Gmail]/Trash
}
標準では無いmailxを起動するように.bashrcの最終行に定義の追加 念のために、PAGERの定義も追加する。
# vi .bashrc

alias mailx=/usr/local/bin/mailx
export PAGER=/usr/bin/less

4. Gmailの参照

下記のコマンドでGmailを読みます。
# mailx -A gmail <---- gmailは、.mailrc で定義したアカウント
正しく設定されていれば、メールの一覧が表示されます。
mailxコマンドのオプション詳細

postfixの設定

1. Gmailに接続するには、認証用のモジュールをインストールする。

下記をインストール
# yum install cyrus-sasl
# yum install /usr/bin/c_rehash

2. Gmailの証明書を取得

# openssl s_client -connect smtp.gmail.com:465 -showcerts
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
i:/C=US/O=Google Inc/CN=Google Internet Authority
ここから
-----BEGIN CERTIFICATE-----

-----END CERTIFICATE-----
ここまでを/etc/pki/tls/certs/google.pem として保存
1 s:/C=US/O=Google Inc/CN=Google Internet Authority
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
ここから
-----BEGIN CERTIFICATE-----

-----END CERTIFICATE-----
ここまでを/etc/pki/tls/certs/euqifax.pem として保存

3. postfixの認証データを作成

証明書をハッシュ化
# c_rehash /etc/pki/tls/certs
Doing /etc/pki/tls/certs
google.pem => xxxxxxxx.0
euqifax.pem => xxxxxxxx.0

4. gmailのパスワードファイル作成

# vi /etc/postfix/auth_passwd
[smtp.gmail.com]:587 xxxxx@gmail.com:password
# chown root. /etc/postfix/auth_passwd
# chown 600 /etc/postfix/auth_passwd
ファイルを更新したらpostmapを必ず実行
# postmap /etc/postfix/auth_passwd

5. Postfixの設定

# vi /etc/postfix/main.cf
relayhost = [smtp.gmail.com]:587
smtp_use_tls = yes

smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/auth_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_mechanism_filter = plain

smtp_tls_CApath = /etc/pki/tls/certs
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache
Postfixを再起動
# /etc/init.d/postfix restart これで設定は終了です。

6. メール送信の確認

# mailx -s "TEST"  XXXXX@gmail.com
テストメールを送ってみます。
.
EOF

エラーがないか/var/log/maillogを確認ください。
# tail -1000 /var/log/maillog

参考URL