なつかしのコナミコマンド

おまとめ三行

カスタムコマンドを作成してそれをクーロンで実行できるようにしてみたい
「management」「commands」というフォルダ
全てのファイルやメソッドをどこからでも同じように呼べるのが便利
今日はカスタムコマンドを使ってみたいと思います。カスタムコマンドというのはバッチ処理などを行う時に使用する、CakePHPで言うところのShellみたいな機能です。今回はカスタムコマンドを作成してそれをクーロンで実行できるようにしてみたいと思います。



カスタムコマンドファイル設置

カスタムコマンドのファイルはアプリケーションフォルダの中に「management」というフォルダを作成し、さらにその中に「commands」というフォルダを作成し、その中に作成します。

/normnois
  ├ /app
  │   ├ /management
  │   │   └ /commands
  │   │      └ sample.py
  │   │    
  │   ├ __init__.py
  │   ├ admin.py
  │   ├ apps.py
  │   ├ models.py
  │   ├ tests.py
  │   └ views.py
  │
  ├ /normnois
  │   ├ __init__.py
  │   ├ settings.py
  │   ├ urls.py
  │   └ wsgi.py
  │
  └ manage.py

normnoisやappは僕が自分でつけたプロジェクト名やアプリケーション名です。図の上の方に「management」「commands」というフォルダがあるのが分かると思います。今回はその中に「sample.py」というカスタムコマンドファイルを作成しました。



sample.pyの中身

とりあえずsample.pyの中にこんなコードを書いてみます。

#sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand) :
  def handle(self, *args, **kwargs) :
    print('無事に動いたよ!')

ファイルの中に「Command」というクラスを作り、その中に「handle」という関数を書きました。クーロンなどで呼び出した時はこのhandle()の中の処理が動きます。

モデルの読み込みなどはビューから呼び出す時と同じです。

#sample.py
from django.core.management.base import BaseCommand
from app.models import Users

class Command(BaseCommand) :
  def handle(self, *args, **kwargs) :
    data = Users.get_data()
    print(data.name)

#model.py
from django.db import models

class Users(models.Model) :
  def get_data() :
    return Users.objects.get(id = 1)

usersというテーブルがあったとして、そこから一件データを取っていると思ってください。



コマンドラインから実行

上記のsample.pyをコマンドラインから起動する方法は簡単です。

$ python3 manage.py sample

サーバーを起動する時とほとんど書き方は同じです。ここではmanage.pyとだけ書いちゃってますが、実際にはプロジェクトフォルダのmanage.pyまでのパスを書きます(/var/www/normnois/manage.pyみたいな)。その後ろの「sample」はカスタムコマンドのファイル名です。

クーロンで実行する場合もこの書き方でオッケーです。

0 0 0 * * * /usr/bin/python3 /var/www/normnois/manage.py sample

これで毎日夜中の0時にsample.pyが起動されるようになります。「/usr/bin/python3」はpythonコマンドファイルの絶対パスです。人によっては違うかもしれません。コマンドもpython3じゃなくてpythonの場合もあると思います。どこにあるか分からない場合はwhichコマンドを使えばパスが分かります。

$ which python3



引数について

引数を渡したい場合は「add_arguments()」という関数を作成します。

#sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand) :
  def add_argument(self, parser) :
    parser.add_argument('parameters', nargs = '+', type = str)

  def handle(self, *args, **kwargs) :
    print(kwargs['parameters'])

#コマンド
$ python3 manage.py sample args1 12345

#出力結果
['args1', '12345']

「parser.add_argument()」がどういうものかってのをあまりまだ理解できてないので、すみませんが詳細な説明は省略で。とりあえずこう書けばkwargs引数の中に値を渡すことができます。typeをstring型に指定しているので、今回の場合は引数に数字を渡しても文字列型として受け取ることになります。



独自にオプション引数を作成することもできます。

#sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand) :
  def add_argument(self, parser) :
    parser.add_argument('parameters', nargs = '+', type = str)
    parser.add_argument('-opt', nargs = '+', type = str)

  def handle(self, *args, **kwargs) :
    print(kwargs['parameters'])
    print(kwargs['opt'])

#コマンド
$ python3 manage.py sample args1 12345 -opt hello

#出力結果
['args1', '12345']
['hello']

こんな感じです。「-opt」という引数用のキーを自分で定義し、そこにhelloという値を渡しています。

add_argumentによって引数を使えるようにした場合、デフォルトだと引数の指定が必須となるので、引数がないとエラーになってしまいます。

#sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand) :
  def add_argument(self, parser) :
    parser.add_argumetn('-opt', nargs = '+', type = str)

  def handle(self, *args, **kwargs) :
    print(kwargs['opt'])

#コマンド
$ python3 manage.py sample

#出力結果
manage.py sample: error: the following arguments are required: -opt

必ずしも引数を指定するとは限らない場合はrequiredをFalseにしておきましょう。

#sample.py
from django.core.management.base import BaseCommand

class Command(BaseCommand) :
  def add_argument(self, parser) :
    parser.add_argumetn('-opt', nargs = '+', required = False, type = str)

  def handle(self, *args, **kwargs) :
    print(kwargs['opt'])

#コマンド
$ python3 manage.py sample

#出力結果
None






DjangoはCakePHPと違い、全てのファイルやメソッドをどこからでも同じように呼べるのが便利ですよね。CakePHPのシェルだとモデルは簡単に呼び出せても、コントローラーやコンポーネントを呼び出すのに一手間必要だったりしますから。