0
2

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のsettings.pyとurls.pyを環境に応じて分割・切り替えよう!

Last updated at Posted at 2023-03-05

概要

Djangoのsettings.pyにいろんな設定を書いていくうちに肥大化していくかと思います
また、settings.py内でif文でDEBUGがTrueもしくはfalseの時に設定を切り替えることができますがかなり冗長になってしまう上にどれが共通の設定でどれが本番環境用か直感的にわかりずらいなと私自身感じていました
そこで、今回は環境別(例えばlocal用、dev用)に

  • settings.py
  • urls.py

の分割および切り替えをする方法について解説したいと思います

前提

  • .envファイル内に環境変数を入れるのと環境構築を容易にするためにDockerを使うことを推奨します
  • Djangoのrunserverなどの基本的なコマンドをある程度知っていること
  • Swaggerを使用するためにdrf-spectacularを使用しますが、設定方法については詳細に説明しません

ファイル構成

settings.pyとurls.pyを分割する際のファイル構成は以下の通りです
プロジェクト名はproject, アプリケーション名はapplicationとします

  • settings.py
  • urls.py

を分割していきましょう
また、settings.pyの分割に伴い、プロジェクトの設定ファイルのパスが変わってしまうため、

  • manage.py
  • asgi.py
  • wsgi.py

もあわせて修正する必要があリます

ディレクトリ構成
└── application
    ├── application
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   ├── models
    │   ├── urls.py
    │   ├── utils
    │   └── views
    ├── manage.py
    ├── poetry.lock
    ├── project
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings
    │   │   ├── __init__.py
    │   │   ├── base.py
    │   │   ├── local.py
    │   │   └── dev.py
    │   ├── urls
    │   │   ├── __init__.py
    │   │   ├── base.py
    │   │   └── local.py
    │   └── wsgi.py
    └── pyproject.toml                                                          

settings

settings.pyを

  • base.py(共通の設定)
  • local.py
  • dev.py

に分割していきます

base.py

base.pyに共通の設定を記載していきます
私はrest_frameworkとカスタムユーザのModelを使用していますが共通で使用したいアプリケーションなどは基本的にこちらに記載していきます

project/settings/base.py
"""
Django settings for application project.

Generated by 'django-admin startproject' using Django 4.1.3.

For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/
"""

import os
from pathlib import Path

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


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/

# SECRET_KEYを.envから取得
SECRET_KEY = os.environ.get("SECRET_KEY")

# ALLOWED_HOSTSを.envから取得
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS").split()

# Application definition

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.BasicAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ]
}


INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "application.apps.ApplicationConfig",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "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",
            ],
        },
    },
]

WSGI_APPLICATION = "project.wsgi.application"


# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
# 今回はMySQLをDBとして使用します
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        # コンテナ内の環境変数をDATABASESのパラメータに反映
        "NAME": os.environ.get("MYSQL_DATABASE"),
        "USER": os.environ.get("MYSQL_USER"),
        "PASSWORD": os.environ.get("MYSQL_PASSWORD"),
        "HOST": os.environ.get("MYSQL_HOST", "db"),
        "PORT": os.environ.get("MYSQL_PORT", 3306),
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/

LANGUAGE_CODE = "ja"

TIME_ZONE = "Asia/Tokyo"

USE_I18N = True

USE_TZ = True


# STATIC_ROOTを設定
STATIC_ROOT = "/static/"
STATIC_URL = "/static/"

# Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# カスタムユーザModelの設定
AUTH_USER_MODEL = "application.User"

local.py

local環境でのみ使う設定を記載していきます
今回はlocal用にSwaggerを使うのでSwaggerの設定はlocal.pyにのみ記載していきます
Swagger以外に下記のように検証用で使うMailCatcherなどの設定も記載します
また、デバッグモードでDjangoを使用したいので

DEBUG = True

にします

project/settings/local.py
"""LOCAL環境用の設定"""
# 共通の設定をimport
from .base import *

DEBUG = True

# REST_FRAMEWORKのdictにSwaggerの設定を追加するためにupdateを使用
REST_FRAMEWORK.update({"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema"})

SPECTACULAR_SETTINGS = {
    "TITLE": "プロジェクト名",
    "DESCRIPTION": "詳細",
    "VERSION": "1.0.0",
}

INSTALLED_APPS += [
    "drf_spectacular",
]

# 後述するurls.pyをlocal用・dev用に分ける際にlocal用を使用
ROOT_URLCONF = "project.urls.local"

# Djangoのメールの設定
EMAIL_HOST = "mail"
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
# SMTPの1025番ポートを指定
EMAIL_PORT = 1025
EMAIL_USE_TLS = False

dev.py

dev環境でのみ使う設定を記載していきます
例えばAWSのSESやS3の設定などは以下のように記載します

project/settings/dev.py
"""DEV環境用の設定"""
from .base import *

DEBUG = False
# 後述するurls.pyをlocal用・dev用に分ける際に今回はbase(local・dev共通)用を使用
ROOT_URLCONF = "project.urls.base"

INSTALLED_APPS += [
    "django_ses",
    "storages",
]

# SESの設定
EMAIL_BACKEND = "django_ses.SESBackend"
AWS_SES_REGION_NAME = os.environ.get("AWS_SES_REGION_NAME")
AWS_SES_REGION_ENDPOINT = os.environ.get("AWS_SES_REGION_ENDPOINT")
DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL")
# S3の設定
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
STATICFILES_STORAGE = "storages.backends.s3boto3.S3StaticStorage"
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME")

urls

続いてurls.pyを

  • base.py(共通の設定)
  • local.py

に分割していきます

base.py

共通で使用するurlを記載します
adminのパスがないとDjangoが動いてくれないので必ず記載しましょう

project/urls/base.py
"""application URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("application.urls")),
]

local.py

Swaggerのパスは開発でのみ使用するのでこちらに記載します

project/urls/local.py
"""LOCAL環境用のURL"""

from django.urls import path
from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularRedocView,
    SpectacularSwaggerView,
)
# project.urls.baseからurlpatternsをimportする必要があります
from project.urls.base import urlpatterns

urlpatterns += [
    # Swaggerの設定
    path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
    path(
        "api/docs/",
        SpectacularSwaggerView.as_view(url_name="schema"),
        name="swagger-ui",
    ),
    path(
        "api/redoc/",
        SpectacularRedocView.as_view(url_name="schema"),
        name="redoc",
    ),
]

manage.py

DJANGO_SETTINGS_MODULEにsettings.pyのパスを記載しますがsettings.pyを分割してしまったので
project.settings.localに変更します
また、.envファイルなどにDJANGO_SETTINGS_MODULEの値を格納している方は忘れずに変更しておきましょう

application/manage.py
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.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()

asgi.py

同様にproject.settings.localに変更します

application/application/asgi.py
"""
ASGI config for project project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.local")

application = get_asgi_application()

wsgi.py

同様にproject.settings.localに変更します

application/application/wsgi.py
"""
WSGI config for project project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.local")

application = get_wsgi_application()

実際に動かしてみよう!

local用

.envファイル内のDJANGO_SETTINGS_MODULEの値でDEBUGモードを分岐させることができます
local用でSwaggerを使えるか確認するために以下のようにします

.env
DJANGO_SETTINGS_MODULE=project.settings.local

127.0.0.1/api/docs にアクセスすると以下のようにSwaggerが表示されたら成功です

スクリーンショット 2023-03-05 10.58.24.png

dev用

dev用でSwaggerが使えないことを確認するために以下のようにします

.env
DJANGO_SETTINGS_MODULE=project.settings.local

127.0.0.1/api/docs にアクセスすると以下のようにNot Foundと表示されたら成功です

スクリーンショット 2023-03-05 11.07.20.png

entrypoint.shによる分岐

シェルスクリプトを使ってDJANGO_SETTINGS_MODULEの値別に切り替える際は以下のようにすることもできます

entrypoint.sh
#!/bin/sh
set -eu

poetry run python manage.py makemigrations
poetry run python manage.py migrate

if [ $DJANGO_SETTINGS_MODULE = "project.settings.local" ]
then
    poetry run python manage.py runserver 0.0.0.0:8000
else
    poetry run gunicorn project.wsgi:application --bind 0.0.0.0:8000
fi

まとめ

以上のやり方でsettings.py内に無駄なif文の分岐を使わずに環境別の設定が直感的にわかるようになリました
プロダクトが大きくなるにつれてsettings.pyが見づらくなってしまうのが悩みだったので対処法がわかってよかったです

0
2
0

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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?