[Python][Django] manage.pyにコマンドを追加する方法
概要
manage.pyに新しいコマンドを追加し、Djangoの環境内で任意の処理を行えるようにします。例えば、以下のようなことができるようになります。
$ PYTHONPATH=.. python manage.py hello Hello
環境
この記事の内容は、Ubuntu Linux 6.10, Python 2.4.4c1, Django 0.97-pre-SVN-6843で確認しました。
やり方
概要に記したように、"Hello"を表示する"hello"コマンドを追加するとします。ここで、以下のように、projectディレクトリにappアプリケーションがあるものとします(settings.pyの設定は、完了しているとします)。また、カレントディレクトリはprojectディレクトリであるとします。
project
-- __init__.py | |
-- app | |
-- __init__.py | |
-- models.py | |
`-- views.py | |
-- manage.py | |
-- settings.py |
まず、project.appパッケージの下に、management.commandsパッケージを追加します。すなわち、以下のようにディレクトリとファイルを作成します。
project `-- app `-- management |-- __init__.py `-- commands `-- __init__.py
新しく作成したふたつの__init__.pyは、空で構いません。
次に、app/management/commandsディレクトリに、<コマンド名>.pyというファイルを作成します。今回の例なら、hello.pyです。ひとつのコマンドにつき、ひとつのファイルが必要です。
app/management/commands/hello.pyには、django.core.management.base.BaseCommandクラスを継承したCommandクラスを記述します。このクラスの名前を、Command以外にすることはできません(このため、ひとつのファイルにはひとつのコマンドしか記述できません)。Commandクラスには、handleメソッドを記述します(以下)。
# -*- coding: utf-8 -*- from django.core.management.base import BaseCommand class Command(BaseCommand): def handle(self, *args, **options): print "Hello" # vim: tabstop=4 shiftwidth=4 expandtab softtabstop
以上により、manage.pyからhelloコマンドが使用できます。ただし、settings.pyのINSTALLED_APPSに設定されたproject.appパッケージをDjangoが見つけられるように、環境変数PYTHONPATHに親ディレクトリを設定して実行します。
$ PYTHONPATH=.. python manage.py hello Hello
詳細
Commandクラスの継承元となるクラスは、BaseCommandクラスの他に、AppCommandクラスとLabelCommandクラス、NoArgsCommandクラスがあります。また、いくつかのクラス変数を記述することで、オプションを指定したり、ヘルプを記述したりすることができます。
BaseCommandクラス
BaseCommandを継承したクラスのhandleメソッドのargsとoptionsには、作成したコマンドに渡された引数とオプションが設定されます。また、handleメソッドがTrueと判定される値を返した場合、その値が標準出力に出力されます。
BaseCommand以外のクラスは、すべてのこのクラスを継承しています。
AppCommandクラス
アプリケーションごとになんらかの処理を行う場合、このクラスを継承します。コマンドは、
$ PYTHONPATH=.. python manage.py [オプション] <アプリケーション名> ...
のようにして実行します。
コマンドの処理内容は、handleメソッドではなく、handle_appメソッドに記述します。
def handle_app(self, app, **options): pass
handle_appメソッドのapp引数には、コマンドラインで指定されたアプリケーションのmodelsモジュールが設定されます(もしmodelsモジュールがなかった場合、このコマンドは実行できません)。
django.core.management.commandsパッケージにあるファイルをみると、モデルからSQLを出力するコマンドが、このクラスを継承しているようです。
LabelCommandクラス
名前を指定してなんらかの処理を行う場合、このクラスを継承します。コマンドは、
$ PYTHONPATH=.. python manage.py [オプション] <名前> ...
のようにして実行します。
コマンドの処理内容は、handleメソッドではなく、handle_labelメソッドに記述します。
def handle_label(self, label, **options): pass
handle_labelメソッドのlabel引数には、コマンドラインで指定された名前が設定されます。
django.core.management.commandsパッケージでは、startappコマンドやstartprojectコマンドが、このクラスを継承しています。
NoArgsCommandクラス
実行する際になんの引数も必要としない場合(オプションはあっても構いません)、このクラスを継承します。
コマンドの処理内容は、handleメソッドではなく、handle_noargsメソッドに記述します。
def handle_noargs(self, **options): pass
このクラスを継承したコマンドに引数を与えると、例外が発生します。
クラス変数
argsクラス変数を設定すると、そのコマンドのヘルプに、設定した内容が追加されます。例えば、
class Command(BaseCommand): args = "foo"
とすると、
$ PYTHONPATH=.. python manage.py hello --help (略) usage: manage.py hello [options] foo (略)
となります。
helpクラス変数には、そのコマンドの説明を記述できます。これは、--helpオプションでみることができます。
class Command(BaseCommand): help = u"Helloを表示します。"
とした場合、
$ PYTHONPATH=.. python manage.py hello --help (略) Helloを表示します。 (略)
となります。
option_listクラス変数には、コマンドが取り得るオプションを、optparse.make_option関数を使って指定します。このとき、継承元のクラスのオプションも表示されるように、親クラスのoption_list属性に追加して設定します。例えば、以下のようにします。
from optparse import make_option class Command(BaseCommand): option_list = BaseCommand.option_list + (make_option("--foo", action="store", dest="foo_option", default="bar", help=u"fooオプション"), )
このとき、ヘルプに、
--foo=FOO_OPTION fooオプション
が表示され、handleメソッドのoptions引数のfoo_optionキー(make_option関数のdestパラメータで指定した名前です。オプションそのものの名前ではありません)に、オプションの値が設定されます。