軽量マイクロフレームワークBottleの魅力を探る
はじめに:Bottleとは? 🤔
Bottleは、Pythonで書かれた、高速かつシンプル、軽量なWSGIマイクロWebフレームワークです。2009年にMarcel Hellkamp氏によって開発が始められました。その最大の特徴は、Python標準ライブラリ以外に依存関係がなく、単一のbottle.py
ファイルのみで構成されている点です。これにより、非常に簡単に導入でき、デプロイも容易になります。
Bottleの哲学は「シンプルさ」と「軽量さ」にあります。機能は最小限に抑えられていますが、ルーティング、テンプレートエンジン、組み込みの開発用HTTPサーバーなど、Webアプリケーション開発に必要な基本的な機能はしっかりと提供されています。特に、Web開発の初学者や、小規模なアプリケーション、API、プロトタイピングなどを迅速に開発したい場合に最適なフレームワークと言えるでしょう。
- 軽量・高速: 依存関係が少なく、動作が速い。
- シンプル:
bottle.py
という単一ファイルで構成。学習コストが低い。 - ルーティング: デコレータを使った簡単なURLルーティング。
- テンプレートエンジン: Pythonicな組み込みテンプレートエンジン(SimpleTemplate)を提供。Jinja2なども利用可能。
- ユーティリティ: フォームデータ、ファイルアップロード、Cookie、ヘッダーなどへの簡単なアクセス。
- 開発サーバー: 開発用のHTTPサーバーを内蔵。
- WSGI準拠: Gunicorn, uWSGI, Pasteなど、様々なWSGIサーバーで動作可能。
- プラグインシステム: 機能拡張のためのプラグイン機構を持つ。
Bottleのインストール 🛠️
Bottleのインストールは非常に簡単です。Pythonのパッケージマネージャであるpip
を使用するのが一般的です。プロジェクトごとに仮想環境を作成し、その中にインストールすることを推奨します。
# 仮想環境を作成 (例: venv という名前で作成)
python3 -m venv venv
# 仮想環境を有効化 (Windowsの場合は venv\Scripts\activate)
source venv/bin/activate
# Bottleをインストール
pip install bottle
または、Bottleの公式サイトからbottle.py
ファイルを直接ダウンロードし、プロジェクトディレクトリに配置するだけでも利用可能です。依存関係がないため、これだけで動作します。
wget https://bottlepy.org/bottle.py
BottleはPython標準ライブラリ以外に依存関係がないため、非常に手軽に始められます。
Hello World! 👋 – Bottleを使ってみる
まずは定番の “Hello World!” アプリケーションを作成してみましょう。以下のコードをapp.py
などのファイル名で保存します。
from bottle import route, run, template
# ルーティング設定: '/hello' または '/hello/<name>' へのアクセスを処理
@route('/hello')
@route('/hello/<name>')
def index(name='World'):
# テンプレートを使ってHTMLを生成
return template('<h1 class="title is-1 has-text-centered has-text-primary">Hello {{name}}!</h1>', name=name)
# 開発用サーバーを起動
# host='localhost' はローカルからのアクセスのみ許可
# port=8080 は使用するポート番号
# debug=True はデバッグモードを有効化(エラー表示や自動リロード)
# reloader=True はコード変更時にサーバーを自動再起動
run(host='localhost', port=8080, debug=True, reloader=True)
このスクリプトを実行します。
python app.py
ブラウザで http://localhost:8080/hello
または http://localhost:8080/hello/Bottle
にアクセスしてみてください。”Hello World!” または “Hello Bottle!” と表示されれば成功です🎉
コード解説:
from bottle import route, run, template
: Bottleから必要な関数(route
,run
,template
)をインポートします。@route('/hello')
/@route('/hello/<name>')
: デコレータを使ってURLパスと関数を結びつけます。/hello
または/hello/<name>
というURLにアクセスがあった場合に、直下のindex
関数が呼び出されます。<name>
はURLの一部を変数として受け取るための「ワイルドカード」です。def index(name='World'):
: URLに対応する処理を行う関数(リクエストハンドラ)です。ワイルドカード<name>
の値が引数name
として渡されます。デフォルト値として’World’を設定しています。return template('<h1>Hello {{name}}!</h1>', name=name)
:template
関数を使ってHTML文字列を生成し、返却します。{{name}}
の部分が引数name
の値で置き換えられます。run(host='localhost', port=8080, debug=True, reloader=True)
: 開発用の組み込みサーバーを起動します。debug=True
にすると、エラー発生時に詳細な情報が表示され、reloader=True
にするとコードを変更して保存した際に自動でサーバーが再起動されるため、開発が捗ります。(本番環境ではdebug=False
,reloader=False
に設定し、WSGIサーバーを使用することが推奨されます。)
ルーティング (Routing) 🧭
ルーティングは、特定のURLパスへのリクエストを、対応するPython関数(リクエストハンドラ)に振り分ける仕組みです。Bottleでは@route()
デコレータを使用して簡単に設定できます。
基本的なルーティング
from bottle import route, run
@route('/')
def home():
return '<p>Welcome to the Home Page!</p>'
@route('/contact')
def contact_page():
return '<p>This is the Contact Page.</p>'
# run(...) は省略
上記の例では、/
へのアクセスはhome
関数、/contact
へのアクセスはcontact_page
関数が処理します。このような固定されたパスを静的ルート (Static Routes)と呼びます。
動的ルート (Dynamic Routes)
URLの一部を可変にしたい場合、ワイルドカードを使用します。ワイルドカードは<変数名>
の形式で記述し、その部分にマッチした文字列が関数の引数として渡されます。
from bottle import route, run, template
@route('/user/<username>')
def show_user_profile(username):
# username変数にはURLの<username>部分が入る
return template('<h2 class="subtitle">Profile page for: {{user}}</h2>', user=username)
@route('/post/<post_id:int>')
def show_post(post_id):
# post_idは整数(int)のみにマッチし、関数には整数型で渡される
assert isinstance(post_id, int)
return template('<p>Showing post number {{pid}}</p>', pid=post_id)
@route('/files/<filepath:path>')
def serve_static_file(filepath):
# filepathはスラッシュを含むパス全体にマッチする
return template('<p>Serving file: {{fp}}</p>', fp=filepath)
# run(...) は省略
ワイルドカードにはフィルタを指定できます。<変数名:フィルタ名>
のように記述します。
:int
: 数字(整数)にのみマッチし、関数には整数型で渡されます。:float
: 浮動小数点数にのみマッチし、関数には浮動小数点数型で渡されます。:path
: スラッシュ (/
) を含む残りのパス全体にマッチします。ファイルパスなどに便利です。:re
: 正規表現にマッチします。例:<param:re:[a-z]+>
フィルタを使用しない場合、ワイルドカードはスラッシュを含まない任意の文字列(1文字以上)にマッチします([^/]+
相当)。
HTTPメソッドの指定
デフォルトでは、@route()
はGETリクエストのみを受け付けます。他のHTTPメソッド(POST, PUT, DELETEなど)を許可するには、method
引数を指定します。
from bottle import route, request, run
@route('/login', method='GET')
def login_form():
return '''
<form action="/login" method="post" class="box">
<div class="field">
<label class="label">Username</label>
<div class="control">
<input class="input" type="text" name="username" placeholder="e.g. Alex Smith">
</div>
</div>
<div class="field">
<label class="label">Password</label>
<div class="control">
<input class="input" type="password" name="password" placeholder="********">
</div>
</div>
<button class="button is-primary">Sign in</button>
</form>
'''
@route('/login', method='POST')
def do_login():
username = request.forms.get('username')
password = request.forms.get('password')
if username == 'admin' and password == 'password': # 実際には安全な認証を行うこと!
return "<p class='has-text-success'>Login successful! Welcome back, admin.</p>"
else:
return "<p class='has-text-danger'>Login failed. Please try again.</p>"
# run(...) は省略
GETリクエストで/login
にアクセスするとログインフォームが表示され、フォームを送信(POSTリクエスト)するとdo_login
関数が処理を行います。request.forms
については後述します。
複数のメソッドを許可する場合は、リストで指定します。
@route('/item/<id>', method=['GET', 'POST'])
def handle_item(id):
if request.method == 'GET':
# GETリクエストの処理
return f"Displaying item {id}"
elif request.method == 'POST':
# POSTリクエストの処理
item_data = request.forms.get('data')
return f"Updated item {id} with data: {item_data}"
リクエストデータの処理 📥
Webアプリケーションでは、ユーザーからのリクエストに含まれるデータ(クエリパラメータ、フォームデータ、ヘッダー、Cookieなど)を取得する必要があります。Bottleではrequest
オブジェクトを通じてこれらの情報にアクセスできます。
from bottle import request, route, run
@route('/search')
def search():
# クエリパラメータ (?keyword=python&limit=10)
keyword = request.query.get('keyword', 'default') # 'keyword'パラメータを取得、なければ'default'
limit = request.query.get('limit', '10') # 'limit'パラメータを取得、なければ'10'
# クエリパラメータ全体を辞書として取得
all_query_params = request.query.decode() # デコードして通常の辞書に
return f'<p>Searching for: {keyword}, Limit: {limit}</p><p>All params: {all_query_params}</p>'
@route('/submit', method='POST')
def submit_form():
# POSTされたフォームデータ
name = request.forms.get('name')
email = request.forms.get('email')
# ファイルアップロード
upload = request.files.get('upload')
if upload:
# ファイルを保存するなどの処理 (安全なファイル名を使用すること!)
# upload.save('/path/to/save/' + upload.filename, overwrite=True)
file_info = f"Uploaded file: {upload.filename} ({upload.content_type})"
else:
file_info = "No file uploaded."
# Cookieの取得
session_id = request.get_cookie('session_id', secret='some-secret-key') # 署名付きCookieの場合secretを指定
# ヘッダーの取得
user_agent = request.headers.get('User-Agent')
return f"""
<div class="box">
<p>Received Form Data:</p>
<ul>
<li>Name: {name}</li>
<li>Email: {email}</li>
</ul>
<p>{file_info}</p>
<p>Session ID from Cookie: {session_id}</p>
<p>User Agent: {user_agent}</p>
</div>
"""
# run(...) は省略
主なrequest
オブジェクトの属性:
request.query
: URLのクエリパラメータ (?key=value&...
) を格納する辞書ライクなオブジェクト。.get(key, default)
で値を取得。request.forms
: POSTリクエストのフォームデータ (application/x-www-form-urlencoded
またはmultipart/form-data
) を格納する辞書ライクなオブジェクト。request.json
: リクエストボディがJSONの場合、パースされたPythonオブジェクトを返す。 (Content-Typeがapplication/json
の場合)request.files
: アップロードされたファイル (multipart/form-data
) を格納する辞書ライクなオブジェクト。値はFileUpload
オブジェクト。request.cookies
: クライアントから送信されたCookieを格納する辞書ライクなオブジェクト。.get(key)
や.get_cookie(key, secret=...)
で取得。request.headers
: HTTPリクエストヘッダーを格納する辞書ライクなオブジェクト。request.method
: リクエストのHTTPメソッド ('GET'
,'POST'
など)。request.url
: 完全なリクエストURL。request.path
: リクエストされたパス部分。request.remote_addr
: クライアントのIPアドレス。
⚠️ セキュリティ注意: ユーザーからの入力は常に信頼できないものとして扱ってください。特にファイル名やフォームデータなどを処理する際は、適切な検証(バリデーション)やエスケープ処理を行い、セキュリティリスク(XSS、ファイルパス操作など)を防ぐ必要があります。
レスポンスの生成 📤
リクエストハンドラ関数は、クライアントに返すレスポンスを生成します。単純な文字列以外にも、様々な形式でレスポンスを返すことができます。
JSONレスポンス
Pythonの辞書やリストを返すと、Bottleは自動的にJSON形式に変換し、Content-Type
ヘッダーをapplication/json
に設定してくれます。
from bottle import route, run
@route('/api/data')
def api_data():
data = {
'id': 123,
'name': 'Example Item',
'tags': ['python', 'bottle', 'web']
}
return data # 自動的にJSONに変換される
# run(...) は省略
リダイレクト
他のURLにリダイレクトさせたい場合は、redirect
関数を使用します。
from bottle import route, redirect, run
@route('/old-page')
def old():
redirect('/new-page', 301) # 301 Moved Permanently
@route('/new-page')
def new():
return "<p>This is the new page!</p>"
# run(...) は省略
HTTPステータスコードの変更
デフォルトではステータスコード200 OKが返されますが、response
オブジェクトを使って変更できます。
from bottle import route, response, run
@route('/notfound')
def not_found():
response.status = 404
return "<h1 class='title is-1 has-text-danger'>404 Not Found</h1>"
@route('/create', method='POST')
def create_item():
# ... (アイテム作成処理) ...
response.status = 201 # 201 Created
return {'status': 'created', 'id': 999} # JSONレスポンスも可能
# run(...) は省略
Cookieの設定
response.set_cookie()
メソッドでクライアントにCookieを送信できます。
from bottle import route, request, response, run
@route('/setcookie')
def set_cookie():
username = request.query.get('name', 'guest')
# 'username'という名前のCookieを設定 (有効期限1時間)
response.set_cookie('username', username, max_age=3600)
# 署名付きCookie (改ざん防止)
response.set_cookie('secure_data', 'important_value', secret='my-super-secret-key')
return f"<p>Cookie 'username' set to {username}</p>"
# run(...) は省略
署名付きCookieは、secret
キーを知らない限り内容を改ざんできないようにするためのものです。
ヘッダーの設定
response.set_header()
やresponse.add_header()
でレスポンスヘッダーを追加・設定できます。
from bottle import route, response, run
@route('/custom-header')
def custom_header():
response.set_header('X-Custom-Header', 'MyValue')
response.content_type = 'text/plain; charset=utf-8' # Content-Typeの設定
return "This response has a custom header."
# run(...) は省略
テンプレートエンジン 🎨
動的なHTMLコンテンツを生成するために、Bottleはテンプレートエンジンを提供しています。PythonコードとHTMLマークアップを分離することで、コードの見通しが良くなり、デザイナーとの分業もしやすくなります。
組み込みテンプレート (SimpleTemplate)
BottleにはSimpleTemplate (stpl) という、高速でPythonicな独自のテンプレートエンジンが組み込まれています。template()
関数や@view()
デコレータで使用できます。
デフォルトでは、Bottleは./views/
ディレクトリからテンプレートファイル(拡張子.tpl
)を探します。テンプレートファイルのパスはbottle.TEMPLATE_PATH
で変更・追加できます。
例: `views/hello_template.tpl`
<!DOCTYPE html>
<html>
<head>
<title>Hello Page</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>
<body>
<section class="section">
<div class="container">
%# Pythonのコメントです
% name = name.title() # 埋め込みPythonコード: nameをタイトルケースに変換
<h1 class="title">Hello {{name}}!</h1>
% if name == 'World':
<p class="notification is-info">This seems to be the default greeting.</p>
% else:
<p class="notification is-success">Nice to meet you, {{name}}!</p>
% end
<h2 class="subtitle">Items List:</h2>
<ul class="list">
% for item in items:
<li class="list-item">{{item}}</li>
% end
</ul>
%# 他のテンプレートをインクルード
% include('footer', copyright_year=2025)
<hr>
<p>Raw HTML (エスケープ無効化): {{!raw_html}}</p>
</div>
</section>
</body>
</html>
例: `views/footer.tpl`
<footer class="footer">
<div class="content has-text-centered">
<p>
© {{copyright_year}} My Awesome App.
</p>
</div>
</footer>
Pythonコード (`app.py`)
from bottle import route, view, run, static_file
# テンプレート内で利用する変数を辞書で渡す
@route('/greet/<name>')
@view('hello_template') # 'views/hello_template.tpl' を使用
def greet(name):
items_list = ['Apple', 'Banana', 'Cherry']
raw_html_content = '<b>Bold Text</b>'
# テンプレートに渡す変数を辞書で返す
return dict(name=name, items=items_list, raw_html=raw_html_content)
# 静的ファイル(CSSなど)の配信ルート
@route('/static/<filename:path>')
def send_static(filename):
# './static/' ディレクトリからファイルを配信
return static_file(filename, root='./static/')
# run(...) は省略
# テンプレートを探すパスを追加することも可能
# import bottle
# bottle.TEMPLATE_PATH.insert(0, '/path/to/my/templates/')
@view('hello_template')
デコレータは、関数が返す辞書をテンプレートエンジンに渡し、結果のHTMLをレスポンスとして返します。template('hello_template', name=name, ...)
のように直接関数を使うこともできます。
SimpleTemplateの主な構文:
{{ expression }}
: Pythonの式を評価し、結果をHTMLエスケープして埋め込みます。XSS対策として重要です。{{! expression }}
: 式を評価し、結果をエスケープせずにそのまま埋め込みます。HTMLタグなどを意図的に出力する場合に使用しますが、セキュリティリスクに注意が必要です。% python_code
: 単一行のPythonコードを実行します。<% python_block %>
: 複数行のPythonコードブロックを実行します。%# comment
: テンプレート内のコメント。出力には含まれません。% include('sub_template', **kwargs)
: 他のテンプレートファイルを読み込んでレンダリングし、その結果を埋め込みます。キーワード引数でサブテンプレートに変数を渡せます。% rebase('base_template', **kwargs)
: 現在のテンプレートの内容を、指定したベーステンプレート内の特定のブロックに埋め込みます。レイアウトの継承などに使います。(詳細は公式ドキュメント参照)- 制御構文 (
if/elif/else
,for/while
,try/except
): Pythonと同様の制御構文が利用可能です。% if condition: ... % elif condition: ... % else: ... % end
のように、ブロックの終わりには% end
が必要です。
⚠️ 注意: SimpleTemplateはテンプレート内に任意のPythonコードを埋め込めるため、信頼できないテンプレートファイルをレンダリングすることは非常に危険です。
他のテンプレートエンジン (Jinja2, Makoなど)
Bottleは、Jinja2やMakoといった他の人気テンプレートエンジンの利用もサポートしています。別途ライブラリのインストールが必要です。
pip install jinja2
from bottle import route, jinja2_template as template, run
@route('/jinja/<name>')
def jinja_example(name):
# Jinja2テンプレート (例: views/jinja_example.html) を使用
# jinja2_templateはtemplate()と同じように使える
return template('jinja_example', name=name, items=[1, 2, 3])
# run(...) は省略
# Jinja2テンプレートのパス設定なども可能
Jinja2を使用する場合、テンプレートファイル(例: views/jinja_example.html
)はJinja2の構文で記述します。
静的ファイルの配信 📁
CSS、JavaScript、画像などの静的ファイルは、Webアプリケーションとは別に配信する必要があります。Bottleではstatic_file
関数を使って簡単に実現できます。
from bottle import route, static_file, run
# './static' ディレクトリ内のファイルを配信するルート
@route('/static/<filepath:path>')
def server_static(filepath):
# root で起点となるディレクトリを指定
# download=True にするとダウンロードダイアログが表示される
return static_file(filepath, root='./static/')
# run(...) は省略
上記のコードでは、./static/
ディレクトリを作成し、その中にCSSファイルなどを配置します。例えば、./static/css/style.css
というファイルがあれば、ブラウザからはhttp://localhost:8080/static/css/style.css
というURLでアクセスできるようになります。
HTMLテンプレート内では以下のようにリンクします。
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<script src="/static/js/script.js"></script>
<img src="/static/images/logo.png" alt="Logo">
static_file
関数は、適切なMIMEタイプを推測し、Last-Modified
ヘッダーなどを設定して効率的な配信をサポートします。
💡 本番環境では: 開発中はBottleのstatic_file
で十分ですが、本番環境ではNginxやApacheなどのWebサーバーに静的ファイルの配信を任せる方が、パフォーマンスが高く効率的です。
プラグインシステム 🔌
Bottleのコア機能はシンプルですが、プラグインシステムを利用して機能を追加・拡張できます。データベース接続、認証、セッション管理など、様々なサードパーティ製プラグインが利用可能です。
プラグインはapp.install()
メソッド(またはグローバルなinstall()
関数)でアプリケーションに適用します。
import bottle
from bottle_sqlite import SQLitePlugin # 例: SQLiteプラグイン
from bottle_session import SessionPlugin # 例: セッションプラグイン
app = bottle.Bottle() # アプリケーションインスタンスを作成
# SQLiteプラグインをインストール (dbキーワード引数を有効化)
# dbfileでデータベースファイルを指定
sqlite_plugin = SQLitePlugin(dbfile='/tmp/mydatabase.db')
app.install(sqlite_plugin)
# セッションプラグインをインストール (sessionキーワード引数を有効化)
# cookie_lifetimeはクッキーの有効期間(秒)
session_plugin = SessionPlugin(cookie_lifetime=600)
app.install(session_plugin)
@app.route('/show/<item_id>')
def show(item_id, db): # プラグインによって 'db' キーワードが使えるようになる
# db オブジェクトを使ってデータベース操作
row = db.execute('SELECT * FROM items WHERE id = ?', (item_id,)).fetchone()
if row:
return template('item_details', item=row)
else:
return bottle.HTTPError(404, "Item not found.")
@app.route('/restricted')
def restricted_area(session): # プラグインによって 'session' キーワードが使えるようになる
user = session.get('user')
if not user:
bottle.redirect('/login')
return f"Welcome to the restricted area, {user}!"
@app.route('/login', method='POST')
def do_login(session): # セッションプラグインを使用
username = bottle.request.forms.get('username')
# ... 認証処理 ...
if username: # 認証成功したと仮定
session['user'] = username # セッションにユーザー情報を保存
bottle.redirect('/restricted')
else:
return "Login failed"
# run(app=app, ...) # アプリケーションインスタンスを指定して起動
上記の例では、bottle-sqlite
プラグインとbottle-session
プラグインを使用しています(これらのプラグインは別途pip install bottle-sqlite bottle-session redis
などでインストールする必要があります)。
SQLitePlugin
: ルート関数にdb
というキーワード引数を追加し、SQLiteデータベースへの接続とカーソルを提供します。SessionPlugin
: ルート関数にsession
というキーワード引数を追加し、セッションデータの読み書きを可能にします。(このプラグインはRedisをバックエンドに使用します)
プラグインはルートごとに適用され、特定のキーワード引数をルート関数に追加したり、リクエスト処理の前後にフック処理を実行したりできます。
利用可能なプラグインの例:
- データベース: bottle-sqlite, bottle-sqlalchemy, bottle-mongodb
- 認証・認可: bottle-cork
- セッション管理: bottle-session
- テンプレートエンジン連携: bottle-renderer
- その他: bottle-werkzeug (Werkzeugライブラリ統合), bottle-flash (フラッシュメッセージ) など
プラグインのリストは公式ドキュメントやPyPIで確認できます。また、独自のプラグインを作成することも可能です。
デプロイメント 🚀
開発中はBottle組み込みの開発サーバー(run()
関数)が便利ですが、これはシングルプロセス・シングルスレッドで動作するため、本番環境での使用には適していません。本番環境では、より堅牢で高性能なWSGIサーバーを使用する必要があります。
WSGIサーバー
WSGI (Web Server Gateway Interface) は、Python WebアプリケーションとWebサーバー間の標準インターフェースです。BottleはWSGIに準拠しているため、様々なWSGIサーバー上で動作させることができます。
代表的なWSGIサーバー:
- Gunicorn (Green Unicorn): Unix系OSで広く使われているWSGIサーバー。シンプルで設定が容易。
- uWSGI: 高機能・高性能なWSGIサーバー。多くの設定オプションを持つ。
- Waitress: Windows環境でも動作する純粋なPython製のWSGIサーバー。
- mod_wsgi (Apache): Apache httpdサーバーのモジュールとして動作。
Gunicornでのデプロイ例
まず、Gunicornをインストールします。
pip install gunicorn
次に、アプリケーションファイル (例: app.py
) を修正し、run()
の呼び出しを削除またはif __name__ == '__main__':
ブロックで囲みます。WSGIサーバーは、ファイル内のapplication
という名前のWSGI callable(Bottleインスタンスなど)を探します。
from bottle import Bottle, route, template
# Bottleインスタンスを作成 (グローバルな 'app' ではなくこちらを使う)
application = Bottle()
@application.route('/')
def index():
return template('<h1 class="title">Deployed with Gunicorn!</h1>')
# 本番環境では以下の run() はWSGIサーバーが担当するため不要
# if __name__ == '__main__':
# run(app=application, host='localhost', port=8080, debug=True, reloader=True)
Gunicornを起動します。
# app:application -> app.py ファイル内の application オブジェクトを指定
# -w 4 -> 4つのワーカープロセスを起動
# -b 0.0.0.0:8000 -> 全てのインターフェースのポート8000で待機
gunicorn app:application -w 4 -b 0.0.0.0:8000
これで、Gunicornがポート8000でリクエストを受け付け、Bottleアプリケーションに処理を渡すようになります。
リバースプロキシ (Nginx / Apache)
通常、本番環境ではWSGIサーバーを直接インターネットに公開せず、NginxやApacheなどのWebサーバーをリバースプロキシとして手前に配置します。
リバースプロキシの役割:
- 静的ファイルの配信
- SSL/TLS終端 (HTTPS化)
- 負荷分散 (ロードバランシング)
- リクエスト/レスポンスのバッファリング
- セキュリティ強化 (アクセス制御など)
- Gzip圧縮
Nginxの設定例 (Gunicornがポート8000で動作している場合):
server {
listen 80;
server_name your_domain.com; # あなたのドメイン名
location /static {
alias /path/to/your/project/static; # 静的ファイルディレクトリのパス
}
location / {
proxy_pass http://127.0.0.1:8000; # Gunicornへリクエストを転送
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
この設定により、ポート80へのアクセスはNginxが受け取り、/static
へのリクエストはNginxが直接ファイルを配信し、それ以外のリクエストはローカルのポート8000で動作しているGunicorn(とその先のBottleアプリ)に転送されます。
Heroku, PythonAnywhere, Google App EngineなどのPaaS (Platform as a Service) を利用すると、インフラの管理を任せて簡単にデプロイすることも可能です。
Bottleのユースケース 🎯
Bottleはそのシンプルさと軽量さから、以下のような用途に適しています。
- 小規模なWebアプリケーション: 機能が限定的なシンプルなWebサイトや社内ツールなど。
- Web API (RESTful API) の構築: JSONレスポンスの返しやすさやルーティングのシンプルさがAPI開発に向いています。
- プロトタイピング: アイデアを素早く形にしたい場合。依存関係が少なくセットアップが容易なため、迅速な開発が可能です。
- 学習目的: Webフレームワークの仕組みを理解するための最初のステップとして。コードベースが小さく(単一ファイル)、理解しやすいです。
- 既存システムへの組み込み: ちょっとしたWebインターフェースを既存のPythonスクリプトに追加したい場合など。
大規模で複雑な機能(管理画面、ORM、高度な認証など)が多数必要なプロジェクトには、Djangoのようなフルスタックフレームワークの方が適している場合があります。しかし、Bottleでもプラグインを組み合わせることで、中規模程度のアプリケーション開発は十分可能です。
他のフレームワークとの比較 (Flask, Django) 🆚
Pythonには多くのWebフレームワークが存在します。ここでは、特に比較されることの多いFlaskとDjangoとの違いを簡単に見てみましょう。
特徴 | Bottle | Flask | Django |
---|---|---|---|
タイプ | マイクロフレームワーク | マイクロフレームワーク | フルスタックフレームワーク |
哲学 | 極限までシンプル、単一ファイル | シンプル、拡張性が高い | Batteries Included (全部入り) |
依存関係 | Python標準ライブラリのみ | Werkzeug (WSGI), Jinja2 (Template) | 多数 (自身のコンポーネント) |
学習コスト | 低い | 低い~中程度 | 高い |
柔軟性/自由度 | 高い | 非常に高い | やや低い (フレームワークの規約に従う) |
組み込み機能 | 最小限 (Routing, Template, Server) | 最小限 + α (セッション, テスト支援など) | 豊富 (ORM, Admin, Auth, Formsなど) |
テンプレートエンジン | SimpleTemplate (組み込み), Jinja2など利用可 | Jinja2 (デフォルト), Makoなど利用可 | Django Template Language (DTL), Jinja2なども設定可 |
コミュニティ/ドキュメント | 比較的小さい | 大きい、活発 | 非常に大きい、活発 |
適した用途 | 小規模アプリ, API, プロトタイプ, 学習 | 小~大規模アプリ, API, 柔軟性が求められる場合 | 中~大規模アプリ, 開発速度重視, 管理画面が必要な場合 |
- Bottle vs Flask: どちらもマイクロフレームワークですが、Bottleはよりシンプルで依存関係がありません。FlaskはWerkzeugとJinja2に依存しますが、より多くの拡張機能(Blueprintsによるモジュール化など)や活発なコミュニティを持ち、柔軟性と拡張性に優れています。Bottleの方がわずかに学習コストが低いかもしれませんが、情報量はFlaskの方が多い傾向があります。
- Bottle vs Django: Bottleは最小限の機能を提供するのに対し、DjangoはWeb開発に必要な多くの機能(ORM、管理画面、認証システムなど)を最初から備えています。Djangoは「設定より規約」の考え方で、開発のレールがある程度敷かれているため、大規模開発では生産性が高くなることがあります。Bottleは自由度が高い反面、必要な機能は自分で選択・導入する必要があります。
どのフレームワークを選ぶかは、プロジェクトの規模、要件、開発者の好みや経験によって異なります。シンプルなものから始めたい、あるいはフレームワークの内部構造を学びたいならBottleは良い選択肢です。柔軟性とエコシステムを重視するならFlask、多くの機能がすぐに必要ならDjangoが有力候補となるでしょう。
まとめ 🏁
Bottleは、その驚くほどのシンプルさと軽量さが魅力のPythonマイクロWebフレームワークです。単一ファイルで依存関係がないため、導入やデプロイが非常に簡単で、Web開発の入門や小規模プロジェクト、API開発、プロトタイピングに最適です。
基本的なルーティング、テンプレート、リクエスト/レスポンス処理機能を備え、プラグインシステムによる拡張も可能です。学習コストが低く、フレームワークの仕組みを理解するのにも役立ちます。
🚀 さあ、Bottleを使って、あなた自身のWebアプリケーション開発を今日から始めてみませんか? 🚀
コメント