自分で書いたPythonのコードを、ブラウザから開けるようにしてみたい。学習がある程度進むと、そう思う瞬間がやってきます。
ただ、Webアプリと聞くと急に難しそうに感じませんか。サーバー、HTTP、ルーティングと、知らない言葉が一度に増えるからです。
その最初の一歩としてよく選ばれるのがFlaskです。数行のPythonを書くだけで、自分のパソコンの中に小さなWebサイトが立ち上がります。
この記事では、Flaskが何をしてくれる道具なのかと、最初につまずきやすいところを順番に見ていきます。
Flaskは、Webアプリの骨組みだけを用意してくれる¶
まずは立ち位置をはっきりさせておきましょう。Flaskは公式に、軽量なWSGI Webアプリケーションフレームワークと説明されています。
WSGIとは、PythonのWebアプリと、それを動かすサーバーが会話するための共通ルールです。この決まりに沿っておけば、動かす側のサーバーを後から差し替えられます。
Flaskの特徴は、必要最小限しか用意しないところにあります。データベースの機能も、ログインの仕組みも、標準では付いてきません。
物足りなく聞こえるかもしれませんが、これは意図された設計です。小さく始めて、必要になったものだけ足していけるのがFlaskの良さです。
中身は2つのライブラリの上に乗っています。役割を知っておくと、エラーメッセージを読むときに迷いません。
| 部品 | 役割 |
|---|---|
| Werkzeug | HTTPのリクエストとレスポンスを扱う土台 |
| Jinja | HTMLを組み立てるテンプレート機能 |
| Flask本体 | 2つをまとめて、書きやすい形にする窓口 |
エラーの表示にWerkzeugやJinjaという名前が出てきても、驚かなくて大丈夫です。どちらもFlaskの一部だと思ってください。
そもそもサーバーという言葉が曖昧なままだと、この先が霧の中になります。先に整理しておくと理解が早いはずです。【関連記事】そもそもサーバーって何?Pythonアプリを公開する前に知りたい基礎知識
インストールから、画面が出るまで¶
Flaskは標準ライブラリではないので、pipで入れるところから始めます。プロジェクトごとに仮想環境を分けておくと、後からバージョンで悩まされません。
pip install Flask
執筆時点の最新版は3.1.3で、Python 3.9以降が必要です。学習用に新しくPythonを入れるなら、この条件はまず気にしなくて大丈夫でしょう。
仮想環境の作り方に自信がないときは、こちらで手順をひととおり確認できます。【関連記事】Pythonの仮想環境(venv)って何のためにある?プロジェクトごとに混ぜない管理法
入ったら、いよいよ最小のアプリを書きます。公式ドキュメントに載っている、これ以上削れない形がこちらです。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
hello.py という名前で保存して、次のコマンドを実行してください。ターミナルにアドレスが表示されます。
flask --app hello run
ブラウザで http://127.0.0.1:5000 を開くと、書いた文字がそのまま出ているはずです。ここまで5行、それだけでWebアプリが動いています。
ファイル名をflask.pyにしてはいけない¶
初学者がいちばん最初に踏む地雷がこれです。自分のファイルに flask.py と名前を付けると、本物のFlaskではなく自分のファイルが読み込まれてしまいます。
公式ドキュメントでも、この名前は避けるようはっきり書かれています。原因の分かりにくいエラーになるので、自分のファイルにflask.pyと名付けないとだけ覚えておいてください。
なお、ファイル名を app.py にした場合は --app の指定を省略できます。多くの入門記事が app.py を使っているのは、この決まりがあるからです。
URLと関数を結びつけるのがルーティング¶
Webアプリの中心にあるのは、どのURLでどの処理を動かすかという対応づけです。これをルーティングと呼びます。
Flaskではデコレータを使って、この対応を関数のすぐ上に書きます。見た目がそのまま設計図になるので、後から読み返したときに分かりやすいのが利点です。
@app.route("/about")
def about():
return "このサイトについて"
@app.route("/user/<username>")
def show_user(username):
return f"ようこそ、{username} さん"
@app.route("/post/<int:post_id>")
def show_post(post_id):
return f"記事番号は {post_id} です"
山かっこで囲った部分は、URLの中の変わる場所です。そこに入った値が、そのまま関数の引数として渡ってきます。
<int:post_id> のようにコロンの前へ型を書くと、受け取る値の種類を絞れます。公式ドキュメントで用意されている型は次のとおりです。
| 書き方 | 受け取るもの |
|---|---|
string |
スラッシュを含まない文字列(省略時はこれ) |
int |
正の整数 |
float |
正の小数 |
path |
スラッシュを含んでもよい文字列 |
uuid |
UUID形式の文字列 |
int を指定しておくと、数字以外が来たときにFlaskが自動で404を返します。関数の中で数値かどうかを確かめる処理を書かずに済むわけです。
ところでこの @ の記法、なんとなく使っている人も多いのではないでしょうか。仕組みを知ると応用が効くようになります。【関連記事】Pythonのデコレータ(@)って何?関数の前後で処理を追加する不思議な記法の正体
末尾のスラッシュで挙動が変わる¶
地味ですが、知らないと悩む仕様があります。ルールの末尾にスラッシュを付けるかどうかで、Flaskの反応が変わるのです。
/projects/ のようにスラッシュ付きで登録すると、スラッシュ無しでアクセスした人は自動でスラッシュ付きへ転送されます。ファイルシステムのフォルダに近い扱いです。
反対に /about とスラッシュ無しで登録した場合、/about/ でアクセスすると404になります。同じページが2つのURLで見えてしまうのを防ぐための仕様です。
画面づくりはテンプレートに任せる¶
先ほどの例では、HTMLをPythonの文字列として返していました。これは最初の確認には便利ですが、ページが増えるとすぐに限界がきます。
そこで使うのがテンプレートです。templates というフォルダにHTMLファイルを置き、render_template で読み込みます。
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/user/<username>")
def show_user(username):
return render_template("user.html", name=username)
テンプレート側では、渡した値を二重の波かっこで埋め込めます。見た目の担当をHTMLへ、処理の担当をPythonへと分けられるのが大きな利点です。
そしてもうひとつ、安全面でも効いてきます。Jinjaのテンプレートは埋め込む値を自動でエスケープするので、危険な文字がそのままHTMLとして解釈されずに済みます。
利用者が入力した文字にHTMLのタグが混じっていても、タグとしてではなく文字として表示されます。これがないと、悪意のあるスクリプトを埋め込まれる隙になります。
開発用サーバーを本番で使ってはいけない¶
ここは強調しておきたいところです。flask run で立ち上がるサーバーは、あくまで開発中に手元で試すためのものです。
公式ドキュメントは、開発サーバーを本番環境で使わないよう明確に警告しています。安全性も安定性も効率も、公開を前提とした作りにはなっていないからです。
さらに危ないのがデバッグモードです。--debug を付けるとブラウザ上で対話的なデバッガが使えますが、これはブラウザから任意のPythonコードを実行できる状態でもあります。
私は10年ほどエンジニアとして開発に関わってきましたが、検証用に立てたデバッグモードのサーバーを外向きの回線に置いたまま帰りかけたことがあります。デバッグモードのまま外へ出さない、これは事故に直結するので徹底してください。
本番ではGunicornなどのWSGIサーバーにアプリを渡して動かします。役割の違いを表にまとめておきます。
| 用途 | 使うもの | 想定 |
|---|---|---|
| 手元での開発 | flask run |
自動リロードとデバッガが使える |
| 公開する本番 | GunicornなどのWSGIサーバー | 複数の同時アクセスに耐える |
| 本番の前段 | nginxなどのHTTPサーバー | 通信の入り口として前に置く |
ちなみに、あなたがいま読んでいるこのサイトもFlaskで動いています。実際に使っているのは記事中で触れたのと同じ3.1.3で、公開はGunicornに任せている構成です。
足りない機能は拡張で足していく¶
最小限しか持たないFlaskですが、困ることは意外とありません。用途ごとに拡張ライブラリが用意されているからです。
よく使われるものを挙げておきます。どれも pip install で入り、アプリに登録して使い始められます。
| 拡張 | できること |
|---|---|
| Flask-SQLAlchemy | データベースをPythonのクラスとして扱う |
| Flask-Login | ログイン状態の管理 |
| Flask-Migrate | テーブル構成の変更を記録して反映する |
| Flask-WTF | フォームの組み立てと入力チェック |
このうちFlask-Migrateが担当するマイグレーションは、言葉の意味からつまずきやすいところです。別記事で丁寧に扱っています。【関連記事】マイグレーションってどういう意味?flask db migrateで使う時に理解できるように解説
もうひとつ、公開前に必ず押さえたいのが設定値の扱いです。データベースの接続情報やセッション用の秘密の文字列を、コードに直接書いてはいけません。
こうした値は環境変数から読み込むのが基本です。うっかり公開リポジトリへ載せてしまう事故を防げます。【関連記事】環境変数とは?PythonでAPIキーを安全に扱う.envと os.environ の基本を初心者向けに解説
学ぶ順番に迷ったら¶
最後に、進め方の目安をお伝えします。全部を一度に理解しようとすると、たいてい途中で息切れします。
おすすめは、まず1ファイルで完結する小さなアプリを最後まで動かすことです。ルーティングとテンプレートだけで作れる、簡単なメモ帳のようなもので構いません。
そこまで来てからデータベースへ進むと、学ぶ理由が自分の中にできています。必要になってから覚えたことは、不思議とよく身につきます。
私の経験でも、最初から大きな構成を真似しようとした人ほど早く手が止まる印象があります。動くものを一度作りきる、この体験がその後の速度を決めます。
まとめ¶
Flaskは、Webアプリに必要な最小限だけを用意してくれるフレームワークです。数行から始められて、必要に応じて拡張を足していけます。
覚えることは多くありません。ルーティング、テンプレート、そして拡張の足し方。この3つが分かれば、たいていのものは形になります。
つまずいたときは、まず自分がどの層の話をしているのか確かめてください。Flaskなのか、Jinjaなのか、それとも動かしているサーバーなのか。ここが切り分けられると調査がぐっと速くなります。
そして公開するときは、開発用サーバーのまま外に出さないこと。この一点だけは、手を抜かないでください。
まずは flask --app hello run を一度打って、ブラウザに文字が出るところまで試してみましょう。
ここまでお読みいただきありがとうございました。