Django開発について参考書やネットでいろいろ調べていると、開発時に意識すると良いベストプラクティスがあることが分かりました。このベストプラクティスの中には開発を始めるときに意識しないと後々修正しづらいものが幾つかあります。そのため、開発を始めるときに意識したほうが良いベストプラクティスに焦点を当てて、その設定内容を備忘録としてまとめました。
今後は、Djangoで開発するときには、意識したいと思います。
本記事は動作環境に依存しないように記載したつもりですが、一応自分が動作確認した環境を記載しておきます。環境は以下のようにConoHa VPS上でUbuntuサーバを立ち上げ、Docker上にDjangoコンテナを構築しています。(環境構築については「docker-composeで「https-portal+Django+postgreSQL」を立ち上げる」でまとめたので、よろしければご覧になってください。)
- ConoHa VPS (メモリ 1GB/CPU 2Core/SSD 100GB)
- Ubuntu 18.04.4 LTS
- Docker: 19.03.8 (2020/3/22 執筆時点の最新)
- Docker Compose: 1.25.4 (2020/3/22 執筆時点の最新)
- https-portal: 1 (2020/3/22 執筆時点の最新)
- Python: 3.8 (2020/3/22 執筆時点の最新)
- Django: 3.0.4 (2020/3/22 執筆時点の最新)
- psycopg: 2.8.4 (2020/3/22 執筆時点の最新)
- uWSGI: 2.0.18 (2020/3/22 執筆時点の最新)
- postgres: latest (2020/3/22 執筆時点では、12.2が最新)
目次
Django開発開始時のベストプラクティス
- 設定ディレクトリをconfigなど分かりやすい名称にする
- staticディレクトリを作成する
- シークレットな変数や環境依存する変数は.envファイルに書く
- フェーズごとに設定ファイルを切り替えられるようにする
- アプリケーションごとにurls.pyを配置する
- ベーステンプレートを使用する
上記のベストプラクティスをデフォルトの構成から見直した結果、以下のように変更となります。
【ベストプラクティス変更前(デフォルトの設定)】
# 以下のコマンドを実行したことを想定
$ django-admin startproject mysite
$ python manage.py startapp apl
mysite
|-- apl (<- アプリケーションディレクトリ)
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- migrations
| | `-- __init__.py
| |-- models.py
| |-- tests.py
| `-- views.py
|-- manage.py
`-- mysite (<- 設定ディレクトリ)
|-- __init__.py
|-- asgi.py
|-- settings.py
|-- urls.py
`-- wsgi.py
【ベストプラクティス変更後】
mysite
|-- .env
|-- app (<- アプリケーションディレクトリ)
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- migrations
| | `-- __init__.py
| |-- models.py
| |-- tests.py
| |-- urls.py
| `-- views.py
|-- manage.py
|-- config (<- 設定ディレクトリ)
| |-- __init__.py
| |-- asgi.py
| |-- settings
| | |-- __init__.py
| | |-- base.py
| | |-- local.py
| | |-- production.py
| | `-- staging.py
| |-- urls.py
| `-- wsgi.py
|-- static (<- staticディレクトリ)
| `-- admin
| |-- css
| |-- fonts
| |-- img
| `-- js
`-- templates (<- templatesディレクトリ)
|-- common
| `-- base.html
`-- app
`-- index.html
- プロジェクト名は任意の文字列ですが、以下では
mysite
を想定します。
設定ディレクトリをconfigなど分かりやすい名称にする
デフォルトの設定では、設定ディレクトリがプロジェクト名と同じになってしまい、ディレクトリの構成が分かりにくいです。そのため、設定ディレクトリをconfig
という名称にします。
プロジェクト作成時に以下のようなコマンドを実行します。
$ mkdir mysite
$ cd ./mysite
$ django-admin startproject config .
- 実行すると、設定ディレクトリが
config
という名称でできるはずです。
これにより、デフォルトのディレクトリ構成から以下のような構成に変更されるはずです。
mysite
|-- manage.py
`-- config (<- 設定ディレクトリ)
|-- __init__.py
|-- asgi.py
|-- settings.py
|-- urls.py
`-- wsgi.py
staticディレクトリを作成する
staticファイルはAPPサーバからではなくWebサーバから返すような構成が一般的です。
DjangoではSTATIC_ROOTを設定することで、staticファイル専用のstaticディレクトリを定義することができます。
STATIC_ROOTを設定するのは、「mysite/config/setting.py」内に以下を追加します。
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
その後、以下のコマンドを入力することでstaticディレクトリが作成されます。
$ python manage.py collectstatic
これにより、デフォルトのディレクトリ構成から以下のような構成に変更されるはずです。
mysite
|-- manage.py
|-- config (<- 設定ディレクトリ)
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
`-- static (<- staticディレクトリ)
`-- admin
|-- css
|-- fonts
|-- img
`-- js
シークレットな変数や環境依存する変数は.envファイルに書く
シークレットな変数や環境依存する変数は、1つのファイルにまとめて一括管理したほうが良いです。
そのような変数を管理するファイルが.envと呼ばれるファイルです。
.envを読み込むPythonライブラリとして、「python-dotenv」がオススメです。
以下のコマンドでインストールできます。
$ apt-get install python-dotenv
.envに記載する内容は、APIトークンやパスワードなどのシークレットな情報や、DBの設定、デバッグ設定などフェーズによって異なる情報等を記載します。
記載の仕方は、以下のような感じです。
ENV_STATE=local
DB_NAME=testDB
DB_USER=hogehoge
DB_PASSWORD=password12345
DB_PORT=5432
.envファイルは、どこに配置してもいいのですが、この記事ではプロジェクトディレクトリ直下に配置して、「mysite/.env」を想定します。これにより、デフォルトのディレクトリ構成から以下のような構成に変更されるはずです。
mysite
|-- .env
|-- manage.py
|-- config (<- 設定ディレクトリ)
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
`-- static (<- staticディレクトリ)
`-- admin
|-- css
|-- fonts
|-- img
`-- js
フェーズごとに設定ファイルを切り替えられるようにする
デフォルトの状態では、設定ファイルは「mysite/config/setting.py」にまとめられています。
しかし、設定ファイルに記載されているデータベースの情報や、DEBUG有無、許可するHOSTなどはフェーズが開発中、staging中、production中では異なります。そのため、設定ファイルをフェーズごとに切り替えるために、以下のディレクトリ構成のようにフェーズごとの設定ファイル(local.py,staging.py,production.py)を作成します。
なお、フェーズごとの切り替えは、環境変数ENV_STATE
(local,staging,production)で定義します。
mysite
|-- config
| |-- settings
| | |-- __init__.py
| | |-- base.py
| | |-- local.py
| | |-- production.py
| | `-- staging.py
「mysite/config/settings/base.py」を作成する
「mysite/config/settings/base.py」ファイルは、設定ファイルのベースとなるファイルです。
既存のsetting.pyの内容をそのままコピペして、そこからセキュリティを強化する設定をします。
# 基本はsetting.pyの内容から変更せず。
from dotenv import load_dotenv
load_dotenv('../.env')
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DEBUG = False
# STATIC_URLとSTATIC_ROOTは下位のsettingsファイルに移管するので削除
- 既存のsetting.pyの内容をそのままコピペして、上記内容の部分のみ書き換えるようにしてください。
- dotenvライブラリは環境変数を読むために使用するライブラリです。詳しくは「シークレットな変数は.envファイルに書く」で説明します。
- セキュリティを強化する設定はどうするのか?という議論はあるかと思いますが、ここでは割愛します。
- 「staticディレクトリを作成する」の章で定義した
STATIC_ROOT
は環境依存する内容のため、base.pyからは削除します。また、STATIC_URL
も削除します。
「mysite/config/settings/local.py」を作成する
「mysite/config/settings/local.py」ファイルは、開発時のローカル環境を想定したファイルです。
Djangoのrunserver
コマンドでローカルサーバを実行することを想定します。
import os
from .base import *
DEBUG = True
ALLOWED_HOSTS = ["*"]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
}
}
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static/')]
runserver
コマンドで起動したローカルサーバを想定しているので、DEBUG
は有効、ALLOWED_HOSTS
はすべて許可にしています。- DATABASEの設定はsqliteを使うことを想定していますが、適宜ご自分の環境に変更してください。
なお、USER
、PASSWORD
に設定しているos.environ['XXX']
は環境変数からの読み出しになります。 STATICFILES_DIRS
はrunsever
コマンド実行したときに静的ファイル自動配信機能を読み取りをするディレクトリを指定します。「staticディレクトリを作成する」でstaticディレクトリは「mysite/static」に移動させたため、それを設定します。
「mysite/config/settings/staging .py」を作成する
「mysite/config/settings/local.py」ファイルは、staging環境を想定したファイルです。
import os
from .base import *
DEBUG = True
ALLOWED_HOSTS = ["*"]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ['DB_NAME'],
'PORT': os.environ['DB_PORT']
}
}
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
- stagingなので、
DEBUG
は有効、ALLOWED_HOSTS
はすべて許可にしています。 - DATABASEはPostgreSQLを想定していまが、適宜ご自分の環境に変更してください。
なお、os.environ['XXX']
は環境変数からの読み出しになります。 STATIC_ROOT
はWebサーバ側でstaticファイルを返すために、「staticディレクトリを作成する」で設定したstaticディレクトリを設定します。
「mysite/config/settings/production .py」を作成する
「mysite/config/settings/local.py」ファイルは、production 環境を想定したファイルです。
import os
from .base import *
ALLOWED_HOSTS = ["*"]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['DB_NAME'],
'USER': os.environ['DB_USER'],
'PASSWORD': os.environ['DB_PASSWORD'],
'HOST': os.environ['DB_NAME'],
'PORT': os.environ['DB_PORT']
}
}
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
- 基本は、staging環境と同じですが、
DEBUG
の設定を削除することで、「base.py」の設定内容であるDEBUG=False
としています。
「 mysite/config/settings/__init__.py 」を生成
Pythonでパッケージを定義するために、「__init__.py」を生成します。中身は空でもOKです。
「mysite/config/settings.py」を削除
「settings.py」が残っていると、「/setting」パッケージと競合してしまうため、「mysite/config/settings.py」を削除します。
「mysite/manage.py」と「mysite/config/wsgi.py」を編集
どの設定ファイルが使われるかは環境変数DJANGO_SETTINGS_MODULE
によって定義されており、以下の2つのファイルで定義されています。
runserver
の場合、manage.py- wsgi経由の場合、config/wsgi.py
そのため、「mysite/manage.py」と「mysite/config/wsgi.py」を以下のように変更します。
【mysite/manage.py】
from dotenv import load_dotenv
load_dotenv('../.env')
def main():
# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
envstate = os.getenv('ENV_STATE','local')
if envstate=='production':
# settings/production.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.production')
elif envstate=='staging':
# settings/staging.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.staging')
else:
# settings/local.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')
# ...
- dotenvライブラリは環境変数を読むために使用するライブラリです。詳しくは「シークレットな変数は.envファイルに書く」で説明します。
- 環境変数
ENV_STATE
はlocal/staging/productionのフェーズを定義するものです。
【mysite/config/wsgi.py】
from dotenv import load_dotenv
load_dotenv('../.env')
envstate = os.getenv('ENV_STATE','local')
if envstate=='production':
# settings/production.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.production')
elif envstate=='staging':
# settings/staging.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.staging')
else:
# settings/local.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')
これで「フェーズごとに設定ファイルを切り替えられるようにする」の設定は完了です。これまでの設定によって、デフォルトのディレクトリ構成から以下のような構成に変更されるはずです。
mysite
|-- .env
|-- manage.py
|-- config (<- 設定ディレクトリ)
| |-- __init__.py
| |-- asgi.py
| |-- settings
| | |-- __init__.py
| | |-- base.py
| | |-- local.py
| | |-- production.py
| | `-- staging.py
| |-- urls.py
| `-- wsgi.py
`-- static (<- staticディレクトリ)
`-- admin
|-- css
|-- fonts
|-- img
`-- js
アプリケーションごとにurls.pyを配置する
これまでプロジェクト全体に関わる設定をしてきましたが、アプリケーションの配置についてもベストプラクティスがあります。
urls.pyは「mysite/config/urls.py」とプロジェクト全体に関わるconfig
ディレクトリにありますが、これに集約すると、アプリケーションが増えるにつれて煩雑になってしまいます。そのため、「urls.py」をアプリケーションごとに分離するのが良いです。
まずは、アプリケーションを以下のコマンドで作成します。
$ python manage.py startapp apl
「mysite/config/urls.py」の方から、アプリケーション側の「urls.py」にinclude
関数で委任します。
# mysite/config/urls.py
from django.urls import include,path
urlpatterns = [
path('', include('app.urls')),
# ...
]
include
で指定するのは、アプリケーション側の「urls.py」の名前空間を指定します。
アプリケーション側の「urls.py」には以下のように設定します。
# mysite/app/urls.py
from django.urls import path
from . import views
app_name = 'app'
urlpatterns = [
# ここにアプリケーションごとの設定を記載する
path('', views.IndexView.as_view(), name='index'),
]
app_name
でアプリケーションの名前空間を指定します。urlpatterns
内にアプリケーションごとの設定を記載します。path()
の第三引数のname
に指定した文字列によって「[名前空間名]:[名前空間内のname]
」でURLを逆引きできるようになります。views.IndexView.as_view()
は参考に記述しているだけなので、適宜ご自分の環境に合わせてください。
これまでの設定により、デフォルトのディレクトリ構成から以下のような構成に変更されるはずです。
mysite
|-- .env
|-- app (<- アプリケーションディレクトリ)
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- migrations
| | `-- __init__.py
| |-- models.py
| |-- tests.py
| |-- urls.py
| `-- views.py
|-- manage.py
|-- config (<- 設定ディレクトリ)
| |-- __init__.py
| |-- asgi.py
| |-- settings
| | |-- __init__.py
| | |-- base.py
| | |-- local.py
| | |-- production.py
| | `-- staging.py
| |-- urls.py
| `-- wsgi.py
`-- static (<- staticディレクトリ)
`-- admin
|-- css
|-- fonts
|-- img
`-- js
ベーステンプレートを使用する
Djangoのテンプレートを使うことで、Webアプリの静的な部分を共通で定義することができます。
WebアプリはHTTPで記述されるため、ヘッダーやフッター、CSS、JSの定義などアプリケーションに関係なく静的に定義できるところはベーステンプレートに定義し、アプリケーション依存のところは別テンプレートで定義することが多いです。
そのため、以下のような構成にすることにしました。
`-- templates (<- templatesディレクトリ)
|-- common
| `-- base.html
`-- app
`-- index.html
- テンプレート内の記述は作るアプリケーションによるので、ここでは記述しません。
- 「mysite/templates/common/」ディレクトリにアプリケーション依存しない共通のテンプレートを格納します。「mysite/templates/common/base.html」がベーステンプレートです。
- アプリケーションごとに「mysite/templates/app/」ディレクトリを作成し、アプリケーション依存するテンプレートをそこに格納します。
上記のtemplatesディレクトリをDjangoに認識させるために、「config/setting/base.py」のTEMPLATESに以下のように設定します。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates') # 追加
],
# ...(省略)
},
]
これまでの設定により、デフォルトのディレクトリ構成から以下のような構成に変更されるはずです。
mysite
|-- .env
|-- app (<- アプリケーションディレクトリ)
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- migrations
| | `-- __init__.py
| |-- models.py
| |-- tests.py
| |-- urls.py
| `-- views.py
|-- manage.py
|-- config (<- 設定ディレクトリ)
| |-- __init__.py
| |-- asgi.py
| |-- settings
| | |-- __init__.py
| | |-- base.py
| | |-- local.py
| | |-- production.py
| | `-- staging.py
| |-- urls.py
| `-- wsgi.py
|-- static (<- staticディレクトリ)
| `-- admin
| |-- css
| |-- fonts
| |-- img
| `-- js
`-- templates (<- templatesディレクトリ)
|-- common
| `-- base.html
`-- app
`-- index.html
関連情報
ConoHa上でDockerを導入したり、Webアプリを立ち上げたりした内容を以下の記事でまとめました。こちらもよろしければご覧になってください。
広告
Djangoをやるなら以下の書籍がオススメです。
DjangoでWebアプリを開発するときの要点が分かりやすくまとめられています。
ConoHa VPS
は初期費用不要で月に数百円で利用できる仮想サーバのサービスです。以下のような方には非常にオススメのサービスとなっています!
- 勉強がてらLinuxの環境をちょっと触ってみたい
⇒管理者権限が実行可能なLinuxサーバ環境が構築可能です! - スモールスタートでサービスを提供して、うまくいったら規模をスケールアップしたい
⇒後からメモリサイズやCPU数などのスケールアップ/スケールダウン可能です! - AWSやGCPなどのクラウドサービスは高いので、もっと安くサーバ構築したい
⇒初期費用なし、月数百円(1時間単位も可)で利用可能です!
以上!
コメント