Flaskで爆速プロジェクトスタート！Next.jsやNestJS風のテンプレート生成ライブラリを自作してみた

# はじめに

この記事は、[株式会社システムアイのAdvent Calendar 2024](https://qiita.com/advent-calendar/2024/systemi24) の 23 日目の記事となります。

皆さんは、Django に並ぶ Python の 2 大 Web フレームワークである **Flask** というものをご存知でしょうか？

Flask は Python における Web フレームワークの一つで、Flask プロジェクトをセットアップするコマンド等はなく 1 から全て書かないといけないのですが、それにより非常に自由度や柔軟性が高く、さらに仕様が非常にシンプルで簡単であるため、大変人気な Web フレームワークです！  
（ちなみに私は Django よりも Flask 派です！Flask のシンプルさに惚れてしまい、Django に「ごめん、君には重すぎるんだ」と言い放ったのも懐かしい思い出です（笑））

しかし、ミニマムスタートとはいっても、ORM が必要だったり認証が必要だったりと毎回お決まりの定型部分もある程度存在し、それを毎回準備するのは非常に手間がかかってしまいます！

また、自由度が高いがゆえにディレクトリ構成なども多種多様な流派が存在し、初心者にとってはどれを選べばいいのか迷ってしまうこともあるかと思います！
（Flask は「自由すぎて逆に何をどうすればいいかわからない！」と初心者が戸惑うこともしばしばです。まるで IKEA で棚を買ったら、説明書もなしに無限の部品が付いてきた感覚かもしれません（笑））

そこで今回、Flask の特徴である「柔軟性」や「シンプル性」は残しつつ、Next.js や NextJS などのようにコマンドによって最低限の共通部分を自動生成し、**プロジェクトの初期セットアップを自動化するコマンドツール**を作成してみました！  
そして、そのツールを Python パッケージとして、Python パッケージリポジトリである **PyPI** に公開しました！
（ディレクトリ構成は、影響範囲を狭めそれぞれがシンプルになるよう「各ページ用フォルダ毎に MVC を作成する構成」としました！）

この記事では、このツールの作成過程を詳しく紹介していきます！

# 成果物
- [PyPI パッケージリポジトリ](https://test.pypi.org/project/flask-gen/)

## 説明
- Flask のプロジェクトの初期セットアップを自動化するコマンドツールです！
- コマンドは以下の 3 つです。
  - `flask-gen project プロジェクト名`：Flask プロジェクトの初期セットアップを行います。（※ 現状では、選択肢において全て「y」以外のパターンではバグがあります、、、。『君なら絶対に y を選ぶよね？』と信じて疑わない純粋な心で作られています（今後修正予定です！））
    - ![スクリーンショット 2024-12-23 13.46.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/5822f450-74a9-e76b-0a24-cfa04e2ff071.png)
    - ![スクリーンショット 2024-12-23 13.49.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/c43713ba-40d4-8967-53b5-272b47d41f25.png)
    - ![スクリーンショット 2024-12-23 13.51.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/a1e5fb1f-7a18-d0d9-2a98-5345c516cad9.png)
    :::note info
    おお、コマンド一発でこんなディレクトリが爆誕しました！誰が手作業なんてするものか、というくらい効率的です。
    :::
  - `flask-gen project プロジェクト名 --minimal`：Flask プロジェクトの初期セットアップを行います（最小構成）。
    - ![スクリーンショット 2024-12-23 13.53.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/fe90f3e2-4928-badd-993b-d9bcee6f6c5f.png)
    - ![スクリーンショット 2024-12-23 13.53.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/6aaea04d-b3c2-1d68-37f9-2d4912d948df.png)
    - ![スクリーンショット 2024-12-23 13.54.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/f2bca3f5-3c62-19c8-2d63-05aedc628be0.png)
  - `flask-gen page ページ名`：Flask プロジェクトにページを追加します。
    - ![スクリーンショット 2024-12-23 13.55.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/dadb334f-8866-64dd-01bb-82454d526155.png)
    - ![スクリーンショット 2024-12-23 13.56.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/fc210099-bbb1-d74c-8cf7-fb70fa9847a4.png)
- Flask アプリのエントリーポイントである「'app.py'」を実行すると、Flask アプリが起動します。
  - トップページ：`http://localhost:5000/`
  - 登録ページ：`http://localhost:5000/auth/register`

# 環境
## 開発環境
- macOS Sonoma 14.6.1
- Python 3.12.2
- プロジェクト管理ツール： `uv`
  - 本題とは関係ないので説明は省きますが、超簡単に言うと、Rust 製のツールのため非常に軽量高速で、今まで Python 開発に必要な「パッケージ管理」「バージョン管理」「仮想環境管理」をそれぞれ別々のツールで行っていたものを 1 つにまとめた統合ツールです！
  - また、後述する PyPI に公開する際に「`uv publish`」とするだけになるため、非常に便利です！
- Linter / Formatter： `Ruff`
  - 本題とは関係ないので説明は省きますが、超簡単に言うと、Rust 製のツールのため非常に軽量高速で、今まで Linter や Formatter をそれぞれ別々のツールで行っていたものを 1 つにまとめた統合ツールです！
- ライブラリ類
  - `Typer`：コマンドラインツール作成用ライブラリ
    - 標準ライブラリよりも非常にシンプルで使いやすく、「--help」オプションや説明を自動生成してくれ、かつ型ヒントを使っているため IDE の補完も効きやすい、非常に便利なライブラリです！

## 動作テスト環境
全部は出来てないです、、、。
- macOS Sonoma 14.6.1
- Windows 10 Pro
- Windows 11 Pro
- Linux (KVM で手元にある環境で行っています）
  - Fedora 35
  - AlmaLinux 8.5
  - Ubuntu 20.04
  - Linux Mint 20.3
  - Zorin OS 16
  - Pop!_OS 21.10
  - MX Linux 21
  - Arch Linux 2022.03.01
  - Manjaro 21.2.3
  - KaliLinux 2022.1
  - openSUSE Leap 15.4

# 作成手順
## 1. プロジェクトの作成
まずは、今回開発するプロジェクトを作成します。  
プロジェクト管理ツール「uv」を使ってプロジェクトを作成します。
「uv」をインストールしていない場合は、[公式サイト](https://docs.astral.sh/uv/#getting-started)を参照してインストールしてください。

```bash
### uv をインストールしていない場合
$ curl -LsSf https://astral.sh/uv/install.sh | sh
```

```bash
### 任意のディレクトリに移動
$ cd ~/Develop/

### uv がインストールされているか確認
$ uv --version
$ uvx --version

### プロジェクトを作成（python のバージョン指定）
$ uv init flask-cli --python 3.12

### 作成されたか確認
$ ls -al flask-cli
```

## 2. プロジェクトの初期セットアップ
プロジェクトの初期セットアップを行います。

```bash
### プロジェクトディレクトリに移動
$ cd ~/Develop/flask-cli

### 仮想環境を作成
$ uv sync
```

## 3. セットアップスクリプトの作成
プロジェクトの初期セットアップを行うスクリプトを作成します。  
一般的には、shell スクリプトで作成することが多いですが、環境ごとにコマンドが異なる場合があるため、今回は Python スクリプトで作成します。  
まず、コマンドラインツール作成用ライブラリ「typer」をインストールし、セットアップスクリプト`cli.py`を作成します。

```bash
### typer をインストール
$ uv add typer

### 不要なファイルを削除
$ rm hello.py

### セットアップスクリプトを作成
$ touch cli.py
```

typer を使って、`cli.py` にディレクトリ作成やファイル作成など、ひたすら地道に処理を追加していきます。  
ひたすら地道に１つ１つトライ＆エラーを繰り返しながらコーディングしていくだけで難しいことは何もやっていないので、内容に関してはここでは省略します。  

ファイルの実行は、以下のコマンドで行います。

```bash
$ python cli.py 引数
```

## 4. コマンドの登録
上記で作成したセットアップスクリプトをコマンドとして登録します。
プロジェクトの設定ファイルである `pyproject.toml` に以下の設定を追加します。

```toml
[project.scripts]
flask-gen = "cli:main"
```

この例では、`flask-gen` というコマンド名で登録し、cli.py の main 関数が実行されるようになっています。  
これだけで、ターミナルで `flask-gen` と入力することで、`python cli.py 引数` と Python スクリプトを実行していたのが、`flask-gen 引数` と簡単にコマンドとして実行できるようになります。

## 5. パッケージの公開
作成したコマンドラインツールを PyPI に公開します。

まずは、[PyPI](https://test.pypi.org/account/register/) および [Test PyPI](https://test.pypi.org/account/register/) へアクセスし、アカウントを作成します。

![img_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/c2a3e121-dbaa-d478-1f34-1760c62bb0f5.png)

登録したメールアドレスに届いたメールを確認し、リンクをクリックしてアカウントを有効化します。

次に、[PyPI](https://pypi.org/manage/account/#api-tokens) および [Test PyPI](https://test.pypi.org/manage/account/#api-tokens) へアクセスし、2 要素認証 を有効化し API Tokenを発行します。

![img.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/60674f33-09a5-315f-de91-603215d954b9.png)

API Token の「トークン名」は任意の名前、スコープは「アカウント全体（全プロジェクト）」にします！

![img_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/98c3310c-20e1-58d0-37c4-af42bcc18abb.png)

発行された API Token は**必ずコピーして保存**しておきます！（今後二度と表示されません！）

次に、`uv` を使って PyPI に公開していきます。

```bash
### ビルド
$ uv build

### test PyPI に公開
$ uv publish --publish-url https://test.pypi.org/legacy/ --token トークン
```

https://test.pypi.org/project/パッケージ名/ にアクセスして、公開されているか確認します。

![img (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624808/61b52616-5a69-0a7d-e3f8-3e7c14453759.png)

これで、作成したコマンドラインツールを PyPI に公開することができました！

# おわりに

以上で、Flask のプロジェクトの初期セットアップを自動化するコマンドツールの作成手順を紹介しました！  

今回は、あまり時間もなく、ひとまず全部コマンドを追加した際の挙動のみを確認しましたが、他のパターンの場合にまだバグがあるため、今後も引き続き修正を行っていきます！
他にも、思っていたよりも俺流構成色が強く出てしまったので、もう少し汎用性を持たせるように改善していきたいと思います！
また、正式リリースに向けて、README や PyPI ページの説明も充実させていきたいと思います！  

初めてのコマンド作成で、まだエコシステムに慣れていない部分も多々ありますが、コマンド化と PyPI への公開は非常に簡単で、とても驚きました！  
自分の作成したツールが世の中にパブリックに公開されると、やはりとてもワクワクしますね！  
利用者から提供者になることで、自分のモチベーションも上がり、より良いツールを作成できるようになるので、今後も積極的に公開していきたいと思います！  

最初にコマンドが動かなかった時は、「え、どこがおかしいの？ CLI の闇は深すぎる……」と絶望しましたが、ついに動いた瞬間、テンションが爆上がりしました（笑）。
また、今後は、このツールをもっと汎用的に改善していく予定です！  
この記事を読んで「やってみたい！」と思った方は、ぜひ挑戦してみてください。PyPIに公開するときの感動、ぜひ味わってみてほしいです！  
みんなで「Flask 開発をもっと楽に」していきましょう！

では、最後まで読んでいただき、ありがとうございました〜！
良い Python ライフを〜！！

# 参考文献
- [typer](https://typer.tiangolo.com/)
- [uv](https://docs.astral.sh/uv/)
- [Python スクリプトをコマンド化](https://packaging.python.org/ja/latest/guides/writing-pyproject-toml/)
- [PyPI への公開](https://qiita.com/shimajiroxyz/items/55404dce8b89cef852d9)