Django 最初にやっておけばよかったこと

# 概要
Django プロジェクトの開発初期にやっておくべきだった・やっておいてよかったことのなかでも、
初期対応コストの低さに対して、後から対応するのは若干割高なものにフォーカスしてまとめたいと思います。

前提として、Django 公式の tutorial は終了していることとします。

# django-environ で環境ごとに異なる設定を管理する
[django-environ documentation](https://django-environ.readthedocs.io/en/latest/)

## 何が嬉しい？
環境変数 (もしくは .env ファイル等) を利用して、環境ごとに異なる設定項目をアプリケーションから分離できます。
何らかの環境にデプロイする際には、必ず環境ごとに異なる項目が発生します。 (データベース接続設定など)
環境ごとの settings.py を用意したりするのは大変であったり、それを git リポジトリに含めてしまい情報漏洩するリスクがあるため、環境変数に設定を分離することには大きなメリットがあります。

考え方自体については、The Twelve-Factor App を読んでない方はぜひ以下をご参照ください。
[The Twelve-Factor App III. 設定](https://12factor.net/ja/config)

各値についてはデフォルト値を設定できるので、ローカル環境用の値を設定するとよいと考えています。
(プロジェクト新規参入者が .env ファイルを作らなくてもとりあえず動かせると嬉しい)


## 設定
settings.py に以下の編集をする。

### import して .env を利用するよう行追加
settings.py の頭の方、以下の形にする。

```python
from pathlib import Path

import environ

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env()
env.read_env(Path.joinpath(BASE_DIR, ".env"))
```

### 各値を .env から取得するように変更

```python
SECRET_KEY = env.get_value(
    "SECRET_KEY",
    cast=str,
    default="django-insecure-**************************************************",
)
```

```python
DEBUG = env.get_value("DEBUG", cast=bool, default=True)
```

```python
DATABASES = {"default": env.db(default="sqlite:///db.sqlite3")}
```

その他アプリで利用する変数についても定義を行う。

### tips: settings.py の値を利用する場合は `from django.conf import settings` して使うこと

django/conf.py の settings は以下になっていて、プロジェクトの設定を取りに行ってくれます。
```conf.py
settings = LazySettings()
```

なので、たとえばプロジェクトのパスをたどって
`from mysite import settings` とかしちゃいけません。
コードの再利用性が下がるため。

[Using settings in Python code | Django documentation](https://docs.djangoproject.com/en/5.0/topics/settings/#using-settings-in-python-code)

# ManifestStaticFilesStorage での static ファイル管理
ManifestStaticFilesStorage を利用することで、ユーザのブラウザに残った古いキャッシュによる不具合を抑制できます。

開発の static ファイルとは別に公開用のファイルを生成しファイル名に md5 ハッシュを付与することで、コードのバージョンと 1:1 対応のファイルを生成し URL との紐づけを管理することを可能にしています。

[ManifestStaticFilesStorage | Django documentation](https://docs.djangoproject.com/en/5.0/ref/contrib/staticfiles/#manifeststaticfilesstorage)

変更の度に collectstatic コマンドの実行が必要になりますが、デプロイの手順に含めてしまえばコストとしては低いと考えています。

また、運用する際には admin サイトなどライブラリが利用する static ファイルを収集する必要があるため、collectstatic を行う運用は結局必要になると思います。


## 設定
以下参照 (自分で手順的に書きたい)

[Django と Google Cloud Storage で Cache Busting | by Kunihiko Kido | VELTRA Engineering | Medium](https://medium.com/veltra-engineering/django-cache-busting-with-googlecloudstorage-66aad6dbc92b)

# 例外をログ出力する
Django ではキャッチされなかった例外をログ出力するいい感じの仕組みがないため、対応が必要です。

## 設定
以下参照

[django： 例外(exception)をログに残すようにする - karasuyamatenguの日記](https://karasuyamatengu.hatenadiary.org/entry/20100521/1274399876)

# その他
だんたんと記事のコンセプトから離れて行ってしまうので、対応できていくとうれしいテーマをいくつかあげてみます。

- formatter を利用する
    - black
    - (ruff が流行ってきてる？)
- linter を利用する
    - flake8, pylint (+ pylint-django) を両方使う
        - (こちらも ruff でよさそう)
    - ベストプラクティス方面に導いてくれる
    - pylint はうるさいけど細かいところも指摘してくれる
        - 都度精査して disable しましょう
    - docstring は無理して書かなくていい
        - **モジュール名, クラス名, メソッド名, 関数名を直訳しただけの docstring でお茶を濁すことには意味がない** ですが、linter に指摘されるとやってしまいがち
        - もちろんちゃんと書けていると素敵
- Docker 環境で開発する
    - DB
        - デプロイする環境と同じ RDBMS を利用したい
        - sqlite では動かないクエリやデータ型は多い
        - Docker 使わず sqlite 以外を何とかするのはコストが高い
            - であれば Docker 使おう
    - runserver から卒業したい
        - nginx + uwsgi を利用する
            - (今でも uwsgi って流行ってるのか自信ない。gunicorn が流行ってる？)
- n + 1 問題をチェックする仕組みづくり
    - SQL クエリの発行数が、扱う要素の数だけ増えてしまうなど実装の問題
        - Django ORM であれば、select_related, prefetch_related を使って解決できる
    - リッチな ORM を利用していると DB アクセスについて意識することが少なく、起こりやすい問題
    - まずは django-debug-toolbar を使ってみる

以上、ありがとうございました。