Django触るなら知っとけ -Djangoベストプラクティス-
はじめに
Djangoを多分2.x頃から触り始めて、なんだかんだで今でもよく使っているのでDjango使うならこの辺知っといた方が良いよなぁみたいな基本的なところをまとめます。
また、DRFを利用する方が多いと勝手に思っているためserializers.pyのようなものが一部登場しますがDRFのものとなりますのであまり気にせず。
プロジェクトの作成
Djangoのプロジェクトを作成する際によく以下のようなコマンドで作成するかと思います。
django-admin startproject ${application-name}
上記のようなコマンドで作成すると以下のような構成となり本来は設定周りのファイルが格納されるのにアプリケーション名となり少しややこしくなるため、別途以下のようなコマンドがよく使われます。
※ config .
の「.」を忘れるとconfigというアプリケーション名としてセットアップされるので注意
django-admin startproject config .
設定系
環境毎に分ける
以下のような構成にして環境毎に読み込むファイルを分けてあげると管理しやすいです。
- base.py: 共通設定
- development.py: 開発環境用
- local.py: ローカル用
- production.py: 本番環境用
- test.py: テスト環境用(テスト実行時に設定ファイルを指定して変更可能)
config/
|-settings
| |- __init__.py
| |- base.py
| |- development.py
| |- local.py
| |- production.py
| |- test.py
|-...
※デフォルトの設定ファイルより階層が1つ深くなるためBASE_DIRも修正する必要があります
import os
from datetime import timedelta
from pathlib import Path
import environ # type: ignore [import-untyped]
BASE_DIR = Path(__file__).resolve().parent.parent.parent
以下はlocal.pyを例としますが他のファイルでも同様base.pyを読み込んであげたのちに各環境の設定情報を追記してあげると良いです。
from .base import *
# django debug toolbar
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
また設定ファイルの読み方を切り替える必要があるためmanage.py等でこのようにしてあげると良いです。
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main() -> None:
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
SECRET_KEYについて
DjangoではSECRET_KEYが開発時にそのままハードコーディングされています。本番でもそのまま公開した状態で利用するのはよくないため環境変数等別で管理するようにしましょう。
SECRET_KEY = env("SECRET_KEY")
こちらのSECRET_KEYの生成用のコマンドはDjangoの方でありますので、そちらを利用したセットアップ用のコマンドを作っていたりします。もちろんどこかで管理しても良いのですがローカルで完結させたいところがあったので作成したりしました。
import os
from django.core.management.utils import get_random_secret_key
def generate_env() -> None:
"""
Generate default env file
Generate file name is ".env"
Regenerate django secret key
"""
# Check exist .evn file
is_exist_env_file: bool = os.path.exists(".env")
if is_exist_env_file:
print('already exists ".env"')
else:
secret_key: str = get_random_secret_key()
writelines: list[str] = [
"# Basic Setting\n",
"ALLOWED_HOSTS=*\n",
"INTERNAL_IPS=127.0.0.1\n",
f"SECRET_KEY={secret_key}\n",
"\n",
"# Database\n",
"DB_NAME=db\n",
"DB_USER=admin\n",
"DB_PASSWORD=xxx\n",
"DB_HOST=xxx\n",
"DB_PORT=5432\n",
"\n",
"# Email\n",
"EMAIL_HOST=\n",
"EMAIL_HOST_USER=\n",
"EMAIL_HOST_PASSWORD=\n",
"EMAIL_PORT=\n",
"DEFAULT_FROM_EMAIL=\n",
"\n",
]
with open(".env", mode="w", newline="\n") as f:
f.writelines(writelines)
if __name__ == "__main__":
generate_env()
Templateファイルについて
TemplatesについてはBASE_DIRから探索するような形で以下のようにすることが多いです。
また、各アプリケーション内にもtemplatesフォルダを作成すると探索対象となるのですが基本的にはアプリケーション内にtemplatesフォルダを作る場合にも構成としてはapplication/templates/application/test.html
のようにアプリケーション名フォルダを1つ間に挟んで管理するようにしてバッティングしないで探索できるようにしております。
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR, "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
アプリケーションの作成
Djangoは1プロジェクトの中に複数のアプリケーションを作成して機能を実装していきます。
Djangoを使って開発する上でここをどのように分割するのか、基準はどうするか、などかなり悩むポイントかと思いますが一旦自分はこういう基準で分けますよといったところを軽めにまとめます。
結論、曖昧ですが「依存関係が循環したり、役割が1アプリに集約されすぎないこと」ここだけ意識して分けています。
以下例でアプリ構成は概要で以下となります
サンプルで仮で作成したのですが、何か投稿+アカウント+取引のような形を表現したい場合は中間となるアプリケーション(以下例だとPostManager)のようなものを作成して実装を行なっております。
- Util: 共通リソースを追加(共通利用するField, Serializer等)
- Account: アカウント/プロフィール等
- Corporation: 会社法人情報
- Authenticator: 認証系(法人/アカウント別で認証機能を提供)
- Post: (投稿系)
- PostManager: (投稿したのちの管理)
アプリケーション内のファイル構成
基本と変わらず以下の構成で実装することが多いです。Djangoのロジックをどこに書くのかという課題はよくありますが基本的にModelをFatにすることが多いです。また自分は複数Modelをまたがるようなロジックをservices.pyに関数を作成して他のアプリケーションやカスタムコマンドを作成した際に流用できるようにしたりしております。
application
|- migrations/
|- tests/
|- __init__.py
|- admin.py
|- apps.py
|- models.py
|- serializers.py
|- services.py
|- urls.py
|- views.py
|- __init__.py
|- __init__.py
Test/Lint/Typeの周辺ツール
開発にあたり周辺のテストや静的解析等周辺ツールを入れてたりしますのでこちらも合わせて紹介します。
テスト
pytest-django, pytest-covを利用しています
デフォルトのテストライブラリはunittestベースになるのでpytestを採用しております
フォーマッター
isort, blackを利用しています
blackのみのところも多いかと思いますがisort一旦入れとくと結構良い感じなのでおすすめです
静的解析
flake8, mypyを利用しています
一部型ファイルがないものや、別途インポートの必要があったりしますので注意です(Djangoならdjango-stubs)
タスクランナー
taskipyを利用してタスク登録しています(poetryを利用しており本来タスクランナーとして利用するものではないため)
ER図生成
django-extensionsを利用するとmodelsからER図を生成してくれます。
python manage.py graph_models -a > gen/er.dot
のようなコマンドを利用するファイルを生成してくれます。私はこのように生成して画像ファイルのような形(svg/png)へ自動変換して公開して管理するようにしております。
さいごに
一旦基本的なところをまとめました。
ViewやModel、Serializer、Urls、管理画面のカスタマイズや、バリデーション等もう少し踏み込んだところになると少し複雑になったりしますがこの辺もまた余裕があれば記事にまとめます。
Discussion