Django: 再利用可能なAPPの設定をDjangoRestFrameWorkにならって実装


Django: 再利用可能なAPPの設定をDjangoRestFrameWorkにならって実装

Djangoで開発しはじめて、再利用可能なAPPを作り別のProjectでInstallしたときに、APPで設定しているデフォルトの設定が変更できない....
となったのでアプリケーションごとの設定をproject/settings.pyから変更できる方法を探していました。
Django Rest Framework にいいコードがあったので、参考にやってみました。

簡単な Todo Task Api を例にやってみます。

動くサンプルのRepositoryはこちらから

Django Rest Framework での例

project/settings.py

...

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10
}

...

こんな感じで変更してはりました。
これでPaginationを使ったときに、PAGE_SIZEを変更できたりします。

Todo Task Api での例

では実際に簡単なTodo Task Apiでの動作の例を見てみましょう。

Modelは下記のTaskのみです。

todo_api/models.py

from django.db import models
from .settings import api_settings


class Task(models.Model):
    title = models.CharField(max_length=api_settings.TASK_TITLE_MAX_LENGTH)
    status = models.IntegerField(choices=api_settings.TASK_STATUS_CHOICES)

todo_api.settings.api_settingsがAPPの設定値を参照できるインスタンスです。
api_settings.KEYで値を取得できます。

今回の例では下記の二つをsettings.pyで変更できます。
- title: max_length
- status: choices

例えばTASK_STATUS_CHOICESのデフォルトの設定値がtodo_api/settings.pyで下記の通りに設定されているとします。

todo_api/settings.py

DEFAULTS = {
    'TASK_TITLE_MAX_LENGTH': 30,
    'TASK_STATUS_CHOICES': [
        (1, 'TODO'),
        (2, 'DOING'),
        (3, 'DONE'),
    ]
}

...

これを変更する場合はproject/settings.pyを変更することで可能になります。

project/settings.py

...

TODO_API = {
    'TASK_STATUS_CHOICES': [
        (1, 'TODO'),
        (2, 'DOING'),
        (3, 'DONE'),
        (4, 'NEW_STATUS'),
    ],
}

上記のコードを追加することでタスクのステータスの選択肢にNEW_STATUSを追加することができます。

では実際に詳細なコードを見ていきましょう。

APISettings クラス

Django Rest Frameworkを参考にAPISettings クラスをすごく簡略化して作成しました。

utils/api_settings.py

from django.conf import settings


class APISettings:
    """
    A settings object, that allows API settings to be accessed as properties.

    Set default settings in your app settings.py like this:
        from app_utils.setting import APISettings
        api_settings = APISettings('TODO_API', DEFAULTS)

    For example:

        from todo_api.settings import api_settings
        print(api_settings.TASK_STATUS_CHOICES)

    """

    def __init__(self, setting_root_name, defaults):
        self._setting_root_name = setting_root_name
        self._defaults = defaults
        self._user_settings = getattr(settings, self._setting_root_name, {})

    def __getattr__(self, item):
        if item not in self._defaults:
            raise AttributeError("Invalid {} setting: {}".format(self._setting_root_name, item))

        try:
            return self._user_settings[item]
        except KeyError:
            return self._defaults[item]

getattrメソッドを使用しているのが重要です。

これにより、api_settings.KEYで設定にアクセスすることができます。

self._user_settingsにはproject/settings.pyで設定されているsetting_root_name以下の値が代入されます。

APP側の settings.py

では先ほど作成したAPISettingsを使用してTODO APP用の設定インスタンスの作成をしましょう。

todo_api/settings.py

from utils.app_setting import APISettings

DEFAULTS = {
    'TASK_TITLE_MAX_LENGTH': 30,
    'TASK_STATUS_CHOICES': [
        (1, 'TODO'),
        (2, 'DOING'),
        (3, 'DONE'),
    ],
}

api_settings = APISettings('TODO_API', DEFAULTS)

APISettingsの第一引数に設定のルート名を、第二引数にデフォルトの設定値を渡して生成します。

これで設定が完了しました。

使用する

先ほど作成したTODO_API用の設定を使用するには、Importしてアクセスするだけです。

todo_api/models.py

from .settings import api_settings

print(api_settings.TASK_TITLE_MAX_LENGTH)
# > 30

まとめ

これで、Package化したAPPの設定をProject側のsettings.pyから変更できるようになりました。
流れとしては、
1. APISettingsクラスをコピペ
2. APP設定のルート名を決める
3. APP側のsettings.pyでDefaultを定義
4. APP側のsettings.pyでAPISettingsのインスタンスを作成
5. 使用する

以上です。

動くサンプルのRepositoryはこちらから

ではでは、よいDjangoライフを!