はじめに
このシリーズでは、PythonのWebフレームワークであるFlaskと、テキストから図を生成できるMermaid.jsを組み合わせて、ローカルPCで動作するMermaid簡易エディタを8回に分けて作ります。
完成時には、次のような機能を持つ小さなWebツールになります。
- Mermaidコードを入力する
- 入力内容をリアルタイムで図にする
- Mermaid構文エラーを表示する
.mmdファイルを保存・読み込む- SVGとして出力する
- Mermaidコードをクリップボードへコピーする
- よく使う図をテンプレートから挿入する
ただし、最初から全部を作ることはしません。
Day 1では、機能を追加するための土台だけを作ります。
今回の到達点は非常に明確です。
ブラウザでFlaskアプリを開き、「Mermaid Editor」というページが表示されるところまで。
Mermaid.jsはまだ使いません。
小さなWebツールでも、最初に責務とディレクトリ構造を整理しておくと、その後の機能追加が格段に楽になります。
今回作るもの
最終的なMermaidエディタでは、大きく分けて次の2つの世界が動きます。
Python / Flask側
- Webページを配信する
- 必要に応じてファイルを扱う
- 将来的にテンプレートデータなどを管理する
ブラウザ側
- Mermaidコードを入力する
- Mermaid.jsで図を描画する
- 入力イベントを処理する
- SVGを取得する
- ファイルをダウンロードする
- クリップボードへコピーする
ここで重要なのは、
Mermaidの描画処理をPythonに担当させない
という設計です。
Mermaid.jsはブラウザ上で動作するJavaScriptライブラリです。そのため、図の描画はブラウザ側へ任せます。
Flaskは必要以上に仕事を抱えません。
Day 1では、この境界を意識しながら最小構成を作ります。
Flaskとは何か
FlaskはPythonでWebアプリケーションを作るための軽量なWebフレームワークです。
Flask公式ドキュメントでは、Flaskを軽量なWSGI Webアプリケーションフレームワークとして説明しています。小さく始めやすい一方で、必要に応じてより複雑なアプリケーションへ拡張できます。 Flask
今回Flaskを採用する理由は、Mermaidエディタそのものが目的だからです。
巨大なWebフレームワークを先に理解するのではなく、
Python
↓
Flask
↓
HTML / CSS / JavaScript
↓
Mermaid.js
という比較的小さな構成でWebツールを作れます。
機械設計で例えるなら、必要以上に巨大なユニットを採用せず、必要な機能を持つ最小構成から始める考え方に近いでしょう。
Flaskとブラウザの役割を分ける
今回のシリーズでは、最初から責務を分離します。
| 処理 | 担当 |
|---|---|
| Webページ配信 | Flask |
| HTML生成 | Flask / Jinja |
| 画面レイアウト | HTML / CSS |
| ユーザー操作 | JavaScript |
| Mermaid描画 | Mermaid.js |
.mmd操作 | 主にブラウザ |
| SVG生成 | Mermaid.js / ブラウザ |
| テンプレート管理 | JavaScriptまたはFlask |
この分け方には理由があります。
たとえばユーザーがMermaidコードを1文字入力するたびに、
ブラウザ
↓
Flask
↓
Python
↓
Flask
↓
ブラウザ
と通信する必要はありません。
ブラウザ内だけで、
入力
↓
JavaScript
↓
Mermaid.js
↓
SVG
と処理したほうが単純です。
サーバーで行う必要がない処理は、サーバーへ送らない。
これが今回の設計方針です。
Python仮想環境を作る
Pythonでは、プロジェクトごとに使用するライブラリを分離するために仮想環境を使えます。
標準ライブラリのvenvを使えば、プロジェクト専用のPython環境を作成できます。Python公式ドキュメントでも、仮想環境は他のPython環境から基本的に分離されたパッケージ環境として説明されています。 Python documentation
まず作業用ディレクトリを作ります。
例として次の名前を使います。
mermaid-editor
ターミナルまたはPowerShellで、このディレクトリへ移動します。
mkdir mermaid-editor
cd mermaid-editor
続いて仮想環境を作成します。
python -m venv .venv
.venvというディレクトリが作成されます。
Windows PowerShellでは、一般的には次のように有効化します。
.\.venv\Scripts\Activate.ps1
有効化されると、ターミナルの表示に.venvなどの環境名が現れます。
環境によってPowerShellの実行ポリシーによりActivate.ps1が実行できない場合があります。Python公式ドキュメントもWindowsでは実行ポリシーの設定が必要になる場合があると説明しています。 Python documentation
なお、仮想環境の有効化そのものは必須ではありません。仮想環境内のPythonを直接指定して実行することもできます。
Flaskをインストールする
仮想環境を有効にした状態でFlaskをインストールします。
python -m pip install Flask
インストールできたか確認します。
python -m flask --version
ここでバージョン情報が表示されれば、Flaskを利用できる状態です。
requirements.txtを作る
プロジェクトで必要なPythonパッケージを記録します。
今回はFlaskだけなので、まずはシンプルにします。
Flask
これをrequirements.txtとして保存します。
ディレクトリは次の状態になります。
mermaid-editor/
├── .venv/
└── requirements.txt
別環境では、
python -m pip install -r requirements.txt
とすれば必要なパッケージを導入できます。
ここで重要なのは、
仮想環境そのものではなく、環境を再構築するための情報を残す
ことです。
Python公式ドキュメントでも仮想環境は再作成可能なものとして扱われ、ソース管理へ含めないことが推奨されています。 Python documentation
つまり、
.venv
を別PCへコピーするのではなく、
requirements.txt
から再構築します。
これは再現可能な開発環境を作るうえで重要な考え方です。
Flask最小アプリを作る
プロジェクト直下にapp.pyを作ります。
from flask import Flask, render_templateapp = Flask(__name__)@app.get("/")def index(): return render_template("index.html")
わずか数行ですが、これでWebアプリケーションとして成立します。
Flask公式Quickstartでも、
app = Flask(__name__)
によってFlaskアプリケーションを作り、ルートを関数へ関連付ける構造が基本になっています。 Flask
Flask(__name__)の意味
app = Flask(__name__)
では、Flaskクラスからアプリケーションオブジェクトを生成しています。
__name__を渡すことで、Flaskはアプリケーションの位置を基準としてテンプレートや静的ファイルなどのリソースを探せます。 Flask
@app.get("/")の意味
@app.get("/")
は、
/
へGETリクエストが来た場合に、直後の関数を実行するという指定です。
つまり、
http://127.0.0.1:5000/
へアクセスすると、
index()
が呼び出されます。
render_template()の意味
return render_template("index.html")
はHTMLファイルをテンプレートとして読み込み、ブラウザへ返します。
Flaskは標準構成ではtemplatesディレクトリからテンプレートを探します。 Flask
PythonコードへHTMLを直接大量に書かず、
Python
HTML
CSS
JavaScript
を分離するための第一歩です。
templatesディレクトリを作る
次に、
templates
ディレクトリを作ります。
その中へ、
index.html
を作成します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mermaid Editor</title>
</head>
<body>
<main>
<h1>Mermaid Editor</h1>
<p>Day 1: Flask is running.</p>
</main>
</body>
</html>
ここでのHTMLは意図的に簡単にしています。
Day 1の目的はデザインではなく、
ブラウザ
↓
Flask
↓
index.html
という経路が成立することを確認することだからです。
staticディレクトリを用意する
さらに、
static
ディレクトリを作ります。
Day 1ではまだCSSやJavaScriptを書かなくても構いません。
最終的な構成は次のようになります。
mermaid-editor/
├── .venv/
├── static/
├── templates/
│ └── index.html
├── app.py
└── requirements.txt
Flaskでは、標準構成ならstaticディレクトリを静的ファイル用として利用できます。CSSやJavaScriptなどをここへ配置できます。 Flask
Day 2以降では、たとえば次のように成長していきます。
static/
├── css/
│ └── style.css
└── js/
└── editor.js
最初から責務別の置き場所を考えておくことで、index.htmlへCSSもJavaScriptも全部書き込む状態を避けられます。
Flaskアプリを起動する
プロジェクトのルートディレクトリで次を実行します。
python -m flask --app app run
app.pyという標準的なファイル名ならFlaskが自動検出できる場合もありますが、このシリーズでは何を起動しているのか分かりやすくするため、--app appを明示して進めます。
Flask公式Quickstartでも、flask --app hello runまたはpython -m flaskによる開発サーバーの起動方法が案内されています。 Flask
正常に起動すれば、ターミナルにはローカルURLが表示されます。
一般的な初期状態では、
http://127.0.0.1:5000
です。
ブラウザで開き、
Mermaid Editor
Day 1: Flask is running.
と表示されれば成功です。
開発時はデバッグモードも使える
開発中は次のように起動できます。
python -m flask --app app run --debug
デバッグモードではコード変更時の自動リロードなどが利用できます。
ただし、Flask公式ドキュメントは組み込みデバッガを本番環境で使用しないよう警告しています。デバッガにはブラウザからPythonコードを実行できる機能があるためです。 Flask
したがって、
ローカル開発
→ --debugを利用可能
公開・本番
→ 開発サーバーやデバッガをそのまま使用しない
と区別します。
今回作るMermaidエディタは、まずローカルツールとして進めます。
なぜHTMLをPythonへ直接書かないのか
Flaskでは次のようなコードも動きます。
@app.get("/")def index(): return "<h1>Mermaid Editor</h1>"
最小動作確認だけなら十分です。
しかし、この方法でエディタを作り続けると、
return """<html>...<style>...<script>...</script></html>"""
のようになり、Python、HTML、CSS、JavaScriptの境界が崩れていきます。
今回のシリーズでは、
app.py
→ Webアプリ側の処理
templates/
→ HTML
static/css/
→ 見た目
static/js/
→ ブラウザ側ロジック
という分離を基本にします。
これはファイルを増やすこと自体が目的ではありません。
変更理由が違うものを分離する
ことが目的です。
UIデザインを変えるためにPythonを触る必要はありません。
Mermaid描画ロジックを変えるためにFlaskのルーティングを触る必要もありません。
この分離が、後半の機能追加で効いてきます。
Day 1完成コード
今回必要なPythonコードはこれだけです。
app.py
from flask import Flask, render_templateapp = Flask(__name__)@app.get("/")def index(): return render_template("index.html")
templates/index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mermaid Editor</title>
</head>
<body>
<main>
<h1>Mermaid Editor</h1>
<p>Day 1: Flask is running.</p>
</main>
</body>
</html>
requirements.txt
Flask
そしてディレクトリ構造は、
mermaid-editor/
├── .venv/
├── static/
├── templates/
│ └── index.html
├── app.py
└── requirements.txt
です。
これが今後7回の基礎になります。
動作確認チェックリスト
起動前に次を確認します。
- Pythonが実行できる
.venvを作成した- 仮想環境へFlaskをインストールした
app.pyがプロジェクト直下にあるtemplates/index.htmlが存在するstaticディレクトリが存在する
起動します。
python -m flask --app app run
ブラウザで、
http://127.0.0.1:5000/
を開きます。
ページが表示されればDay 1は完了です。
よくある失敗
No module named 'flask'
代表的な原因は、FlaskをインストールしたPythonと、現在実行しているPythonが違うことです。
まず確認します。
python -m pip show Flask
表示されなければ、
python -m pip install Flask
を実行します。
pipだけでなく、
python -m pip
とすることで、現在使用しているPythonとpipの対応を明確にできます。
TemplateNotFound
たとえば、
jinja2.exceptions.TemplateNotFound: index.html
が出た場合は、ディレクトリ構造を確認します。
正しい基本形は、
app.py
templates/
└── index.html
です。
templateではなく、
templates
であることにも注意します。
Address already in use
5000番ポートを別アプリが使用している可能性があります。
Flask公式Quickstartでも、ポート5000が使用中の場合はアドレス使用中のエラーが発生すると説明されています。 Flask
開発時には別ポートを指定できます。
python -m flask --app app run --port 5001
その場合は、
http://127.0.0.1:5001/
へアクセスします。
flask.pyという名前を付ける
アプリケーションファイルを、
flask.py
と命名するのは避けます。
Flask本体のモジュール名と衝突するためです。Flask公式Quickstartでもこの名前を使わないよう明記されています。 Flask
今回は、
app.py
を使用します。
今回あえて実装しなかったもの
Day 1では、次の機能を実装していません。
- Mermaid.js
- コード入力欄
- 2ペインUI
- リアルタイム描画
.mmd保存- SVG出力
- テンプレート
- API
- データベース
これは不足ではありません。
Day 1の責務ではないからです。
小さなMVPでも、最初から機能を詰め込むと問題発生時に原因を切り分けにくくなります。
今日は、
Python
↓
Flask
↓
HTML
↓
ブラウザ
だけを成立させます。
次回はその上へ、
HTML
+
CSS
+
Mermaid.js
を追加します。
段階ごとに正常状態を確定してから次へ進むことで、問題が起きた場所を特定しやすくなります。
実務ツールとして考える
今回の構成はMermaid専用ではありません。
FlaskがHTMLを配信し、ブラウザ側JavaScriptが主要処理を担当する構成は、小さな社内ツールや設計支援ツールにも応用できます。
たとえば、
入力フォーム
↓
JavaScript
↓
計算
↓
結果表示
だけで済む計算なら、処理をブラウザだけで完結できます。
一方、
入力
↓
Flask
↓
Python計算
↓
結果
とすれば、既存のPython計算ロジックをWeb UIから利用できます。
さらに、
Flask
↓
JSON
↓
Python計算モジュール
と責務を分ければ、将来的に別UIから同じ計算ロジックを再利用することもできます。
つまりFlaskを学ぶ目的は、Webサイトを作ることだけではありません。
Pythonで作ったロジックへ、人間が使いやすいWeb UIを付ける方法を身につけること
にもあります。
Mermaidエディタは、その練習として扱いやすい題材です。
練習問題
練習問題:表示文章を変更する
index.htmlの、
<p>Day 1: Flask is running.</p>
を任意の文章へ変更してください。
Flaskを再起動せずブラウザを再読み込みし、変更が反映されるか確認します。
確認観点
HTMLテンプレートの変更とPythonコードの変更が別物であることを確認してください。
練習問題:新しいURLを追加する
app.pyへ次のルートを追加してみます。
@app.get("/health")def health(): return "OK"
ブラウザから、
http://127.0.0.1:5000/health
へアクセスします。
OKと表示されれば成功です。
確認観点
同じFlaskアプリケーションの中で、
/
と、
/health
が別々のPython関数へ対応していることを確認します。
練習問題:存在しないURLへアクセスする
たとえば、
http://127.0.0.1:5000/not-found
へアクセスしてみてください。
ルートを定義していないため404になります。
確認観点
Flaskは「Python関数を上から順番に実行してページを探している」のではありません。
URLとビュー関数の対応関係、つまりルーティングによって処理を選択しています。
まとめ
Day 1ではMermaidエディタそのものの機能はまだ作っていません。
代わりに、
Python
↓
Flask
↓
templates/index.html
↓
ブラウザ
というWebアプリケーションの最小経路を作りました。
今回押さえておきたいポイントは次のとおりです。
- プロジェクトごとにPython仮想環境を分ける
- FlaskはWebアプリケーションの入口として使う
- HTMLをPythonコードへ埋め込まず
templatesへ分離する - CSSやJavaScriptは
staticへ分離していく - Mermaidの描画はPythonではなくMermaid.jsへ担当させる
- 必要以上の機能をDay 1へ持ち込まない
- 正常動作する最小構成を確定してから機能を追加する
この時点では非常に小さなプログラムです。
しかし、ここで決めた、
入力
描画
保存
出力
テンプレート
を別々の責務として扱う考え方が、後半の実装を支えます。
Day 1の成果物は「Hello World」そのものではありません。
今後7回の変更を受け止められる、最小のアプリケーション骨格を作ったこと
が今回の成果です。
参考情報
Flaskの最小アプリ、ルーティング、テンプレート、静的ファイル、開発サーバーについてはFlask公式Quickstartで確認できます。 Flask
Python仮想環境の作成方法と性質についてはPython公式venvドキュメントを参照してください。

