122
143

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

俺的Djangoベストプラクティス雛形までの一連の流れ

Last updated at Posted at 2018-12-28

俺的Djangoベストプラクティス雛形

Djangoプロジェクト開発において、先人の知恵に大いに助けられた。
まとまりつつある俺的Djangoベストプラクティス雛形を構築するまでの手順を記録しておく。

2019年10月01日 最新版に更新
2020年09月01日 いまだ現役のプラクティス
2023年01月06日 Django 4.1対応版に更新

大まかなキーワード

  • python環境
  • pipenv
  • Django環境
  • 開発版/テスト版/デプロイ版の切り替え, 保守性, git管理
  • アプリケーション開発
  • パッケージ化
  • デプロイ 
  • pipenvの準備, スクリプトで一括処理

python環境の準備 pipenv

python周りの管理はpipenvを使う。
バージョン管理、開発環境の分離、スクリプトの一元管理を叶えてくれる便利な奴。
ハングアップを疑うくらい遅い時がある。

pyenv インストール

pyenvのインストール
$ sudo yum -y install gcc make zlib zlib-devel openssl openssl-devel
$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

pipenv インストール

pipenvはbrewやpipでインストールできる。
便利なので仮想環境でなくグローバルにインストールしていいと思う。

pipenvのインストール
$ pip install pipenv

諸設定を環境変数に登録する。

諸設定を環境変数に設定するために、シェルのプロファイル系ファイルに追記
$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bash_profile # パス通し
$ echo 'export PIPENV_VENV_IN_PROJECT=1' >> ~/.bash_profile # スクリプトでvenvを使いやすくするために、venvをプロジェクトディレクトリ内に作ってもらう設定

pipenvの初期設定

pipenvでpythonを始める。

プロジェクトで使うバージョンの指定
$ pyenv install --list # インストール可能なバージョンの一覧
$ pipenv --python <バージョン>

始めて起動するとPipfileとPipfile.lockができる。
Pipfileのscriptsブロックに任意のスクリプトを記述できる。

Pipfile
[scripts]
init = "pipenv install"
init_dev = "pipenv install --dev"
server = "python manage.py runserver 0.0.0.0:8000"
Pipfileのscriptsの使い方
$ pipenv run スクリプト名

pipenvでpython

次に、仮想環境のON/OFF。

pipenvで仮想環境の有効化
$ pipenv shell # 仮想環境のON 未作成であればこの時に作成
$ exit # 仮想環境のOFF Ctrl + dも可

スクリプトにこれを書くと、仮想環境に入って止まるので、
スクリプト内で仮想環境に入りたい場合は次のように書く

pipenvで仮想環境の有効化(スクリプト)
source .venv/bin/activate # 仮想環境のON
deactivate # 仮想環境のOFF

pipenvでパッケージ管理

基本的な使い方はpipと同じ。
ただし、開発環境にだけインストールしたいものは分離できる。

パッケージインストール
(project)$ pipenv install パッケージ名
(project)$ pipenv --dev install パッケージ名 # 開発環境only

別環境でパッケージを復元するとき、開発用を含めるかどうか選択できる。

パッケージの復元
(project)$ pipenv install
(project)$ pipenv install --dev # 開発環境用パッケージもインストールする

仮想python環境でDjangoプロジェクトを立ち上げる

Djangoをインストール
(project)$ pipenv install django
Djangoプロジェクトを立ち上げる
(project)$ django-admin startproject config .
プロジェクトディレクトリの構造
testDjango
│── config
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── .venv
└── manage.py

settings.pyを実行環境に応じて変更できるようにする

セキュリティやパフォーマンスの観点から、開発版、テスト版、デプロイ版それぞれでALLOWED_HOSTS、データベースの設定、DEBUGなどを切り替えたほうが良い。
環境に適した設定で動作させるために、settings.pyの内容を分割する。
実行時にどの設定ファイルを読み込むかを指定できる。
ファイル名や分割の粒度は任意。

settings.pyの分割
(project)$ mkdir config/settings
(project)$ mv config/settings.py config/settings/base.py # 既存のsettings.pyを分割後の汎用ファイルとする
(project)$ vi config/settings/base.py # 汎用設定ファイルから分割する項目を削除する
(project)$ vi config/settings/develop.py # 開発版だけに適用したい設定を記述
(project)$ vi config/settings/test.py # テスト版だけに適用したい設定を記述
(project)$ vi config/settings/deploy.py # デプロイ版だけに適用したい設定を記述
(project)$ touch config/settings/__init__.py # config/settingsディレクトリをパッケージ化
プロジェクトディレクトリの構造
testDjango
├── config
│   ├── __init__.py
│   ├── settings
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── deploy.py
│   │   ├── develop.py
│   │   └── test.py
│   ├── urls.py
│   └── wsgi.py
├── .venv
└── manage.py

設定ファイルのディレクトリ階層が一つ深くなったのでbase.pyを修正する。

base.py
- BASE_DIR = Path(__file__).resolve().parent.parent
+ BASE_DIR = Path(__file__).resolve().parent.parent.parent

各分割ファイルでbase.pyを読み込むことで、それぞれが一つの完結したsettings.py系ファイルになる。

分割ファイルの書き方(例:deploy.py)
from .base import * # base.pyを読み込む 

# base.pyにない分を補完

DEBUG = False

ALLOWED_HOSTS = ['公開するホスト']

実行時にどの設定ファイルを読み込むか指定すれば環境ごとに異なる設定で実行できる。
デプロイ版ではuwsgiやgunicornで設定ファイルを指定すればいい。

seetingsファイルを指定して実行
(project)$ python manage.py runserver 0.0.0.0:8000 --settings=config.settings.develop

ファイルの指定はパスをドット区切りで記述する。

Pipfileに書いておくと楽できる。

Pipfile
[scripts]
server = "python manage.py runserver 0.0.0.0:8000 --settings=config.settings.develop"
Pipfileのスクリプトで起動
(project)$ pipenv run server

ちなみに、設定ファイル無指定の場合のデフォルト値はmanage.pyに記述してある。
ここを書き換えておくと便利だけど、実行時の指定をサボらないなら必須ではない。

manage.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.develop')

static, templateをプロジェクトディレクトリ直下に配置する

djangoで静的ファイルやテンプレートを呼び出すと、settings系で設定されたBASE_DIR直下にあるstatic,templatesディレクトリとアプリケーションディレクトリ以下にあるstatic,templatesディレクトリを検索し、最初にヒットしたものを使用する。
複数アプリケーションを開発しているとき、静的ファイルやテンプレートの名前が被ると意図しないものを使用してしまう可能性がある。
それを避けるために名前空間を与えておき、指定するデータを確実に特定できるようにするのを通例とするのがDjango公式ドキュメントのチュートリアル
Untitled.png

仕様も言いたいこともアプリケーション毎にまとめたい気持ちも分かるけど、冗長すぎる。
名前空間つけるなら、元より一箇所にまとめればいい。
Untitled (2).png

仕組みは静的ファイルも同じ。
デザイナーがいる場合は切り分けたstatic, templatesだけを渡せばいいから衛生的。

これを実現するための設定は以下のとおり。
ついでに、デプロイ時に静的ファイルを集めるディレクトリも作っておく。(staticsディレクトリ)

settings.pyまたはsettings/base.py
TEMPLATES = [
    {
        〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
    },
]

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'statics')]
# デプロイ用の設定
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # STATICFILES_DIRSで指定されたディレクトリからSTATIC_ROOTにファイルを集めて
STATIC_URL = '/static/' # STATIC_URL上で配信する
プロジェクトディレクトリの構造
testDjango
├── config
├── .venv
├── manage.py
├── static
├── statics
└── manage.py

git管理から除外するファイル

あとはアプリケーションを追加して開発を進めるだけ。
そこで、個人開発でもチーム開発でももはやgit管理は避けられない。
チームで共有すること、デプロイのためのファイル管理をすることを念頭に.gitignoreファイルを作成する。
といっても、除外するのは仮想python環境(.venv)と(もしあるなら)ローカル開発環境用settings系ファイルのみ。
理由は、それぞれ(人、マシンともに)が個別に準備すべきものだから。

.gitignore
.venv/
config/settings/local.py #あれば

あと、後からアプリケーションを追加するとできるmigrationsも追記すべきだと思う。
開発で試行錯誤したmigrationファイルがデプロイ環境に行って実行されてしまうのがよくないかもしれない。

migrationsを.gitignoreに入れてたら、makemigrationsmigrationでアプリケーションを認識しなくなった。
開発環境で試行錯誤したとしても、成功までの履歴をなぞることで最終的にはきれいになるのがmigration機能のいいところなので、
その恩恵を賜る。

プロジェクトディレクトリの構造
testDjango
├── .gitignore
├── config
├── .venv
├── manage.py
├── templates
├── static
├── statics
└── manage.py

アプリケーション models, viewsをパッケージ化する

アプリケーションを作成する。

Djangoアプリケーションの作成
(project)$ python manage.py startapp testApp
プロジェクトディレクトリの構造
testDjango
├── .gitignore
├── config
├── .venv
├── templates
├── static
├── statics
├── manage.py
└── testApp
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    ├── models.py
    ├── tests.py
    └── views.py

ここで気に入らないのがmodels.pyとviews.py。
保守性を高めるためにも、modelごと、viewごとにファイルを分割したい。
そこで、それぞれをパッケージ化する。

models,viewsのパッケージ化
(project)$ mkdir testApp/models
(project)$ touch testApp/models/__init__.py
(project)$ rm testApp/models.py
(project)$ mkdir testApp/views
(project)$ touch testApp/views/__init__.py
(project)$ rm testApp/views.py
プロジェクトディレクトリの構造
testDjango
├── .gitignore
├── config
├── .venv
├── templates
├── static
├── statics
├── manage.py
└── testApp
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    ├── models
    │   └── __init__.py
    ├── tests.py
    └── views
        └── __init__.py

以降、modelはmodelsディレクトリ、viewはviewsディレクトリに追加すればいい。
追加時、__init__.pyにimportすること。
(import必須?詳しい方教えてください。)

デプロイ

いつもの構成はnginx <-> gunicorn <-> Django
デプロイ環境の構築・設定はこちら、Djangoアプリケーション with gunicornのサービス化はこちらを参考にする。
やるべきことは以下4点。

  • git clone(初回以降fetch, mergeでもいいと思う)
  • pipenvでライブラリパッケージのインストール
  • 静的ファイル配信の準備
  • デプロイ環境用DBのmigration

定型なので、スクリプトにしちゃいましょう。

update.sh:デプロイ用スクリプト(デプロイ環境のプロジェクトディレクトリ直下にある想定)
#!/usr/bin/bash

git fetch
git merge origin/master
source .venv/bin/activate
pipenv install
python manage.py collectstatic --noinput
python manage.py makemigrations
python manage.py migrate
deactivate
systemctl restart testDjango

デプロイ環境を更新したい場合は、このスクリプトを実行するだけでOK。

参考

現場で使える Django の教科書《基礎編》
現場で使える Django の教科書《実践編》
EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイする
gunicorn + Flask + nginx + Systemdで動かしてみた
2018年のPythonプロジェクトのはじめかた

122
143
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
122
143

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?