Djangoの管理コマンドを上書き(置換え)する

Djangoフレームワークの管理コマンド(manage.py スクリプトのコマンド)をオーバーライド(上書き、置換え)する方法について。

管理コマンドの登録の仕組み

Djangoのドキュメントには、管理コマンドのオーバーライドについての記載があります。

https://docs.djangoproject.com/en/3.1/howto/custom-management-commands/#overriding-commands

Djangoのコマンドの登録メカニズムは、次の通りです。

  • 最初に組込みのコマンドがセットアップされます
  • INSTALLED_APPS のアプリケーションにコマンドがあれば登録します
    • コマンドはアプリケーションのディレクトリ以下に management.commands 以下のモジュールがロードされ、モジュール内のCommandクラスのインスタンスがコマンドとして登録されます
    • モジュール名がコマンド名として使われます(たとえば、 runserver コマンドなら runserver.py)
    • このとき、同じ名前のコマンドがあれば、アプリのコマンドで先に登録されたコマンドを上書き(オーバーライド)します
  • runserver コマンドについては、Django内部で、 django.contrib.staticfiles が組込みの runserver コマンドをオーバーライドしているので気をつける必要があります

既存のコマンドを置き換えてみる

myapp という名前のアプリケーションで、組込みのコマンド migrate を置き換えてみます。

試したバージョンは、 Python 3.8、 Django 3.1.3 です。

myappのディレクトリ構造は以下の通り:

myapp
├── __init__.py
├── admin.py
├── apps.py
├── management
│     ├── __init__.py
│     └── commands
│         ├── __init__.py
│         └── migrate.py
├── migrations
│     └── __init__.py
├── models.py
├── tests.py
└── views.py

myapp.py:

from django.core.management.base import BaseCommand


class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        print("これはmyappのコマンドです")

組込みのコマンドを置き換える場合、組込みのコマンドを継承する必要は必須ではないです。例のように Command という名前のクラスで、 BaseCommand と同等の振る舞いができれば、コマンドとして利用できます。

実行結果

myappsettings.pyINSTALLED_APPS に登録されていると、組込みの migrate コマンドが myapp アプリのコマンドに置き換わります。

$ ./manage.py migrate
これはmyappのコマンドです