nikkie-ftnextの日記

イベントレポートや読書メモを発信

DjangoでWebアプリを作るPythonプロジェクトをPoetry / uv / Hatchで管理したら

※この記事はDjangoもくもく会: 4回目の成果発表です。

はじめに

叛逆のニジガサキってボーボボだったんだ... nikkieです。

Pythonには依存ライブラリ管理ツールが、たっくさんあります。
DjangoでWebアプリを作るときのツールの選択肢を増やしたく、素振りしました。

目次

前提:DjangoでWebアプリを作る

手前味噌ですが先日の記事から、DjangoでWebアプリを作る場合は「再配布しないプロジェクト」に当たります。

  • ライブラリ(Djangoなど)のインストールが必要
  • 開発しているプロジェクト自体はインストール可能にしない

プロジェクト自体をインストールしないという点について、依存ライブラリ管理ツール(Poetry, uv, Hatch)の理解を深めたく、今回Djangoを題材に素振りしました。
Djangoアプリは最小限でよいので、公式ドキュメントの投票アプリを作り始めた時点のものとしました。

docs.djangoproject.com

ただし、SECRET_KEYを扱うためにdjango-environ1を導入しています2

仮想環境を開発者が管理する場合

依存ライブラリ管理ツールを使わない場合です(すなわち、俺たちが依存管理ツールなんだ!3)。
Django公式ドキュメントDjango Girls Tutorialはこの方法です。

venv-example/
├── .venv/
├── polls_tutorial/
│   ├── config/  # Djangoプロジェクト
│   ├── polls/  # Djangoアプリケーション
│   ├── .env  # django-environ
│   ├── db.sqlite3
│   └── manage.py
├── .python-version  # Pythonはpyenvで管理
├── requirements.in
└── requirements.txt  # pip-compile

好みで、pip-toolsを使いました4

% cd venv-example
% source .venv/bin/activate
% python -V
Python 3.12.6

% cd polls_tutorial
% python manage.py runserver

http://127.0.0.1:8000/polls/

Poetry

過去の素振りから、pyproject.tomlでpackage-modefalseに指定します

[tool.poetry]
name = "poetry-django-example"
version = "0.1.0"
package-mode = false

開発者の方でpython -m venv .venvしておき、この仮想環境をPoetryに使わせます(poetry install)。

poetry-example/
├── .venv/
├── polls_tutorial/  # Djangoプロジェクトとアプリケーション(変更がないので省略)
├── poetry.lock
└── pyproject.toml
% cd poetry-example
% poetry --version
Poetry (version 1.8.2)
% poetry run python -V
Python 3.11.8

% cd polls_tutorial
% poetry run python manage.py runserver

uv

uv 0.4.0からのuv init --appを使っていきます!5
これは「再配布しないプロジェクト」にドンピシャです。

pyproject.tomlは[build-system]を持ちません(想定通り)

[project]
name = "uv-django-example"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.12"
dependencies = [
    "django>=5.1.1",
    "django-environ>=0.11.2",
]
uv-example/
├── .venv/  # uvによる
├── polls_tutorial/
├── .python-version  # uvによる
├── pyproject.toml
└── uv.lock
% cd uv-example
% uv --version
uv 0.4.12 (Homebrew 2024-09-18)
% uv run python -V
Python 3.12.5

% cd polls_tutorial
% uv run python manage.py runserver

Hatch

今回挙げた中で一番動きが予想できなかったツール。
過去にも触っているので初見ではないのですが、「HatchはどんなPythonプロジェクトも一律インストールしてしまうのではないか」と私は認識していました。

Djangoの投票アプリのコードをコピーしてきてからhatch new --init

hatch-example/
├── polls_tutorial/
└── pyproject.toml  # hatch new --init で生成

自動生成されたpyproject.tomlを修正しました。
全体は https://github.com/ftnext/django-project-examples/blob/6364c7b6228c2f92ebd67b4bd9e81237b7b7bd2a/hatch-example/pyproject.toml にあります

必要だったのは以下の指定。

[tool.hatch.build.targets.sdist]
include = ["/polls_tutorial"]

これを書かずにhatch run python -Vとすると

ValueError: Unable to determine which files to ship inside the wheel using the following heuristics: https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection

Hatchはhatch_django_exampleディレクトリを探しますが、これが見つけられなかったために送出されたようです。
エラーメッセージの全文はこちら:https://github.com/pypa/hatch/blob/hatch-v1.12.0/backend/src/hatchling/builders/wheel.py#L235-L246
[tool.hatch.build.targets.wheel]の指定を案内されました。

偶然にもHatchで環境構築しているDjangoプロジェクト django-wiki を知っており、
https://django-wiki.readthedocs.io/en/latest/development/environment.html
そのpyproject.tomlを参考にしました。
関係ありそうに思えたのが、[tool.hatch.build.targets.sdist]
https://github.com/django-wiki/django-wiki/blob/releases/0.11.2/pyproject.toml#L119-L127

[tool.hatch.build.targets.sdist]
include = [
    "/src",
]

これにならって上記のように指定しました。
https://hatch.pypa.io/latest/config/build/#file-selection より「Patterns」にincludeexcludeを指定する例があります(しっかり読んで理解深めたい)

以上で動かせています!

% cd hatch-example
% hatch --version
Hatch, version 1.12.0
% hatch run python -V
Python 3.12.0

% cd polls_tutorial
% hatch run python manage.py runserver

Hatchのことはまだよく分かっていないのですが、今回のDjangoアプリのコードはeditable installされていないかもしれません。
hatch shellで見えるパスのディレクトリ(site-packagesがある)を覗いたところ(過去のunkoの例とは違って)polls_tutorialソースコードはeditable installされていないような??

終わりに

Djangoを使ったPythonプロジェクトを、俺たち / Poetry / uv / Hatch で環境構築しました。

  • 俺たち=公式ドキュメントで案内される方法。仮想環境を用意する
    • 今回は好みのpip-toolsを使ったよ
  • Poetryとuv:Pythonプロジェクト自体をインストールしない設定が可能
    • Poetryはpackage-mode = false(pyproject.tomlの[tool.poetry]
    • uv init --app(pyproject.tomlに[build-system]が書かれない)
  • Hatchは[tool.hatch.build.targets.sdist]を指定してhatch runからDjangoアプリを動かせた
    • Pythonプロジェクト自体はインストールされていない雰囲気(想定と違う。宿題事項)

ソースコードはこちらにあります

uvは簡単さがあると思います。
Poetryは独自仕様オレオレが強い6ので、この2つなら私はuv推し。
Hatchは簡単ではないのですが、私はまだ完全に理解していないようなので、引き続き小さなネタで手を動かしてみようと思います。


  1. 過去記事「Djangoの設定ファイルの分割について知りたく、書籍・記事を調査しました - nikkie-ftnextの日記
  2. DBはSQLite.envDATABASE_URL=sqlite:///db.sqlite3 と指定するのですが、これによりmanage.pyを実行するカレントディレクトリにファイルが置かれるようです(実務寄りではPostgreSQLMySQLのコンテナを用意すると思うので深追いしていません)
  3. 公式ドキュメントのように直接インストールするよりも利点が多いので採用しています
  4. uv init --app --no-readme。できあがるhello.pyは不要なので削除しています
  5. PoetryはPEP 621をサポートしていない