カスタム django-admin コマンドの実装Link to this heading

manage.py を用いることで独自のアクションを登録する事ができます。例として、あなたが配布している Django アプリケーションに manage.py アクションを追加したくなったとします。このドキュメントでは、このチュートリアル で作成した polls アプリケーションに独自の closepoll コマンドを追加します。

それを実現するには、maangement/commands ディレクトリをアプリケーションに追加するだけで済みます。Django はディレクトリ名がアンダースコアで始まらないディレクトリ内の Python モジュール毎に manage.py コマンドを追加します。

Code
polls/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            __init__.py
            _private.py
            closepoll.py
    tests.py
    views.py

この例では、closepoll コマンドは polls アプリケーションを INSTALLED_APPS に含むプロジェクト全てで利用できるようになります。

_private.py モジュールは管理コマンドとして利用できません。

closepoll.py モジュールには一つだけ満たすべき要件が有ります。 -- BaseCommand クラスもしくはその サブクラス の一つを継承した Command クラスを定義する必要が有ります。

コマンドを実装するには、polls/management/commands/closepoll.py を以下のように編集してください:

Code
from django.core.management.base import BaseCommand, CommandError
from polls.models import Question as Poll

class Command(BaseCommand):
    help = 'Closes the specified poll for voting'

    def add_arguments(self, parser):
        parser.add_argument('poll_id', nargs='+', type=int)

    def handle(self, *args, **options):
        for poll_id in options['poll_id']:
            try:
                poll = Poll.objects.get(pk=poll_id)
            except Poll.DoesNotExist:
                raise CommandError('Poll "%s" does not exist' % poll_id)

            poll.opened = False
            poll.save()

            self.stdout.write(self.style.SUCCESS('Successfully closed poll "%s"' % poll_id))

新たに作成したカスタムコマンドは python manage.py closepoll <poll_id> と実行する事で利用できます。

handle() メソッドは一つ以上の poll_ids を受け取り、それぞれに対応した poll.openedFalse にセットします。もしコマンドの利用者が存在しない poll を指定した場合、CommandError 例外が発生します。poll.opened 属性は元の チュートリアル には存在しないので、この例では polls.models.Question モデルに追加しました。

省略可能な引数を受け入れるLink to this heading

ここまで実装した closepoll に対し、別途コマンドラインオプションを受け取ることで指定された投票を閉じる代わりに削除するよう機能追加する事も容易に可能です。これらのオプション機能は add_arguments() メソッドによって以下のように追加できます。:

Code
class Command(BaseCommand):
    def add_arguments(self, parser):
        # Positional arguments
        parser.add_argument('poll_id', nargs='+', type=int)

        # Named (optional) arguments
        parser.add_argument(
            '--delete',
            action='store_true',
            dest='delete',
            help='Delete poll instead of closing it',
        )

    def handle(self, *args, **options):
        # ...
        if options['delete']:
            poll.delete()
        # ...

オプション(例では delete)は handle メソッドで辞書型変数の引数として利用可能です。add_argument の利用についてより詳細な情報を得るには Python 公式ドキュメントの argparse を参照してください。

独自のコマンドラインオプションを追加できるのに加え、management commands に定義された --verbosity--traceback といったオプションも標準で利用できます。

管理コマンドとロケールLink to this heading

標準では、BaseCommand.execute() メソッドは、Django に含まれる一部のコマンドが、プロジェクト全体を通じて中立な文字列の言語を必要とするタスク(例えばエンドユーザーに対するコンテンツ出力やデータベースへのデータ投入など)を実行するため、翻訳処理を無効化しています。

何らかの理由でカスタム管理コマンドから特定のロケールを利用する必要が有る場合は、I18N をサポートしているコードから提供される関数を利用している handle() メソッド内で手動で activate と deactivate を行う必要が有ります。

Code
from django.core.management.base import BaseCommand, CommandError
from django.utils import translation

class Command(BaseCommand):
    ...

    def handle(self, *args, **options):

        # Activate a fixed locale, e.g. Russian
        translation.activate('ru')

        # Or you can activate the LANGUAGE_CODE # chosen in the settings:
        from django.conf import settings
        translation.activate(settings.LANGUAGE_CODE)

        # Your command logic here
        ...

        translation.deactivate()

単純に設定されたロケールをコマンドに利用させ、Django がそれを無効化しないようにしたい状況も考えられます。その状況では BaseCommand.leave_locale_alone オプションを利用する事で望む結果を得ることができます。

ただし先に述べた状況に取り組む場合、多様なロケールでのシステム管理コマンドの実行には細心の注意を払う必要が有る事を考慮しておくべきです。すなわち以下に示す点に注意してください:

  • そのコマンドが実行される際は設定値 USE_I18N が常に True である事を確認してください。(これは Django を処理する動的ランタイム環境が翻訳機能を即座に無効化する事に起因する潜在的な問題の好例です)

  • 作成したコマンドとロケールが変わる事で振る舞いが変わるコードをレビューし、作成したコマンドにおいて予想される振る舞いへの影響を評価してください。

テストLink to this heading

カスタム管理コマンドのテストに関する情報は テストに関するページ で得ることができます。

Overriding commandsLink to this heading

Django registers the built-in commands and then searches for commands in INSTALLED_APPS in reverse. During the search, if a command name duplicates an already registered command, the newly discovered command overrides the first.

In other words, to override a command, the new command must have the same name and its app must be before the overridden command's app in INSTALLED_APPS.

Management commands from third-party apps that have been unintentionally overridden can be made available under a new name by creating a new command in one of your project's apps (ordered before the third-party app in INSTALLED_APPS) which imports the Command of the overridden command.

Command オブジェクトLink to this heading

class BaseCommandLink to this definition

全ての管理コマンドの派生元となる基底クラス。

コマンドライン引数を処理したり応答からコードの該当箇所を洗い出す機構など全てにアクセスしたい場合に利用してください。それらの振る舞いを代える必要が無ければ、:ref:`サブクラス<ref-basecommand-subclasses>`の利用を検討してください。

BaseCommand クラスのサブクラス化には handle() メソッドの実装が必要です。

属性Link to this heading

属性は全て派生クラスでセットでき、BaseCommand クラスの サブクラス で利用可能です。

BaseCommand.helpLink to this definition

コマンドに関する短い説明、ユーザーが python manage.py help <command> を実行する事でヘルプメッセージとして表示されます。

BaseCommand.missing_args_messageLink to this definition

入力が必須の位置引数を定義しており、その引数が失われている場合に返すエラーメッセージを任意に設定する事ができます。デフォルトの出力は argparse による出力("too few arguments")です。

BaseCommand.output_transactionLink to this definition

A boolean indicating whether the command outputs SQL statements; if True, the output will automatically be wrapped with BEGIN; and COMMIT;. Default value is False.

BaseCommand.requires_migrations_checksLink to this definition

ブーリアン値。True の場合、ディスク上に存在する一連のマイグレーション定義がデータベース上に保存されたマイグレーション定義とマッチしない場合に警告を出力します。この警告はコマンドの実行を停止させる物ではありません。デフォルトの値は False です。

BaseCommand.requires_system_checksLink to this definition

ブーリアン値。True の場合、コマンド実行前に Django プロジェクト全体が潜在的な問題を抱えていないかチェックされます。デフォルトの値は True です。

BaseCommand.leave_locale_aloneLink to this definition

A boolean indicating whether the locale set in settings should be preserved during the execution of the command instead of translations being deactivated.

デフォルトの値は False です。

Make sure you know what you are doing if you decide to change the value of this option in your custom command if it creates database content that is locale-sensitive and such content shouldn't contain any translations (like it happens e.g. with django.contrib.auth permissions) as activating any locale might cause unintended effects. See the Management commands and locales section above for further details.

BaseCommand.styleLink to this definition

stdoutstderr を記述した際にカラー出力を補助するインスタンス変数です。以下の利用例を参照ください:

Code
self.stdout.write(self.style.SUCCESS('...'))

カラーパレットの調整と利用可能なスタイルについては Syntax coloring を参照してください(このセクションに記述されている "roles" のアルファベットを大文字にすると利用できます)。

--no-color オプションを渡してコマンドを実行した場合、全ての self.style() 呼び出しはオリジナルのカラー分けされていない出力を行います。

メソッドLink to this heading

BaseCommand has a few methods that can be overridden but only the handle() method must be implemented.

BaseCommand.add_arguments(parser)Link to this definition

コマンドに渡されたコマンドライン引数を操作するパーサーを追加するためのエントリポイントです。カスタム管理コマンドが受け取る位置引数およびオプション引数を追加するためにはこのメソッドをオーバーライドする必要が有ります。直接 BaseCommand を継承している場合は super() の呼び出しは必要有りません。

BaseCommand.get_version()Link to this definition

Returns the Django version, which should be correct for all built-in Django commands. User-supplied commands can override this method to return their own version.

BaseCommand.execute(*args, **options)Link to this definition

コマンドを実行し、必要とされた場合(requires_system_checks 属性によって設定可能)システムチェックを行います。コマンドが CommandError 例外を発生させた場合は、実行を中断して stderr に出力します。

BaseCommand.handle(*args, **options)Link to this definition

コマンドにおける実際の処理内容。サブクラスはこのメソッドを実装しなくてはならない。

It may return a string which will be printed to stdout (wrapped by BEGIN; and COMMIT; if output_transaction is True).

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)Link to this definition

潜在的な問題のために Django プロジェクト全体を検証するシステムチェックフレームワークを利用します。致命的な問題は CommandError 例外を発生し、警告は stderr への出力、重要でない通知は stdout への出力となります。

app_configs および tags が共に None であった場合、全てのシステムチェックが実行されます。tags はチェックタグ、例えば compatibility あるいは models 等、のリストとなります。

BaseCommand のサブクラスLink to this heading

class AppCommandLink to this definition

一つ以上のインストールされたアプリケーションラベルを引数として受け取り、それぞれに対して何らかの処理を行う管理コマンド。

handle() を実装する代わりに、サブクラスでは、アプリケーション毎に一度ずつだけ呼び出される handle_app_config() を実装する必要が有ります。

AppCommand.handle_app_config(app_config, **options)Link to this definition

コマンドラインで渡されたアプリケーションラベル個々に対応している AppConfig のインスタンスである app_config に応じたコマンドの処理を行います。

class LabelCommandLink to this definition

A management command which takes one or more arbitrary arguments (labels) on the command line, and does something with each of them.

Rather than implementing handle(), subclasses must implement handle_label(), which will be called once for each label.

LabelCommand.labelLink to this definition

コマンドに渡される任意引数について記述した文字列。この文字列はコマンドの使用法やエラーメッセージに利用します。デフォルトは 'label' です。

LabelCommand.handle_label(label, **options)Link to this definition

コマンドラインに渡された文字列である label に対応したコマンドの処理を行います。

コマンドが発生させる例外Link to this heading

exception CommandErrorLink to this definition

管理コマンド実行中に発生した問題について示した例外クラス。

コマンドラインコンソールから管理コマンドを実行中にこの例外が送出された場合、その例外は捕捉されて適切な出力ストリーム(例えば stderr 等)に整形されたエラーメッセージを表示させます。結果として、例外の送出は(エラーに関する明快な記述と併せて)コマンド実行中何らかの問題が発生した場合に状態を示す好ましい方法となります。

call_command() を介して管理コマンドが実行された場合は、例外の捕捉をするかどうかは実装に依存します。