Djangoでコマンドを実行する方法

ノウハウ
スポンサーリンク

DjangoでWebアプリ開発中に以下のようなことを思ったことありませんか?

Djangoでコマンド実行をするケース
  • ちょっとしたDjangoの機能テストを実行したい
  • Web経由ではなく、cronなどで周期実行したい

このような場合、Djangoでコマンド実行すれば解決できるかもしれません。
本記事では、Djangoでコマンドを実行する方法をまとめましたので、よろしければ参考にしてください。

 

目次

 


 

手順概要

手順一覧
  • 事前準備
    ・startappで「sample_app」アプリを作る
    ・settings.pyのINSTALLED_APPSに追加
    ・「sample_app」直下に「management」フォルダを作る
    ・「management」の中に「commands」フォルダを作る
    ・「commands」の中に「__init__.py」を作る
    ・「commands」の中に「mycommand.py」を作る
  • コマンドファイルを書いていく
    ・「mycommand.py」にdjango.core.management.base.BaseCommandを継承した
     Commandクラスを作り、必要なメソッドをオーバーライドする
  • コマンドを実行する
    ・「python manage.py mycommand」とすると定義したプログラムを実行できる

なお、以下の手順は以下の環境で動作確認済みです。

・Python 3.11.1
・Django==4.1.4

 

事前準備

事前準備としてコマンド実行用のファイルを作成します。

 

コマンド実行
(Pjojectを作っていなかったら以下を実行)
$ django-admin startproject mysite .
(事前準備)
$ cd mysite
$ python manage.py startapp myapp
$ mkdir -p myapp/management/commands
$ touch myapp/management/commands/__init__.py
$ touch myapp/management/commands/testcommand.py
settings.py(追記)
INSTALLED_APPS = [
    ...
    'myapp.apps.MyappConfig'
]

 

 

フォルダ構成は以下の通り。

.
|-- myapp
|   |-- 省略
|   |-- management
|   |   `-- commands
|   |       |-- __init__.py
|   |       `-- testcommand.py
|-- manage.py
|-- mysite
`-- static
    `-- 省略

 

 

コマンドファイルを書いていく

コマンドファイル(testcommand.py)にはdjango.core.management.base.BaseCommandを継承したCommandクラスを作り、必要なメソッドをオーバーライドします。

 

/myapp/management/commands/testcommand.py
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "テストコマンド"

    def handle(self, *args, **options):
        if options['greeting']:
            print("Hello!")

    def add_arguments(self, parser):
        parser.add_argument('--greeting', action='store_true', help='挨拶する')

ポイントは、

  • BaseCommandを継承したCommandを定義すること
  • Commandクラスの変数helpはpython manage.py help カスタムコマンド名としたときに表示される説明文的なもの
  • handleメソッドを継承すること
    • handleメソッド内に実行した処理を書く

 

 

コマンドを実行する

最後にコマンドを実行してみます。

コマンド実行
$ python manage.py testcommand --greeting
Hello!

$ python manage.py help testcommand
usage: manage.py testcommand [-h] [--greeting] [--version] [-v {0,1,2,3}]
                             [--settings SETTINGS] [--pythonpath PYTHONPATH]
                             [--traceback] [--no-color] [--force-color]
                             [--skip-checks]

テストコマンド

options:
  -h, --help            show this help message and exit
  --greeting            挨拶する
  ...割愛

 

 

コマンドに引数を設定するには?

上の例では、コマンドの引数を想定していませんでしたが、引数も簡単に追加できます。
コマンドに引数を設定するには、parser.add_argument()の引数をいくつか設定すればよいです。

引数説明
第一引数コマンドのオプション名
(例:'--greeting')
nargs引数の数を設定できる(int'?''*''+'などが使える)
default第一引数で定義されたオプションが指定されたなかったときに
この値を使って実行をする。
type引数の型を指定できる(int, strなど)
action引数の処理方法を指定できる
('store_true'を指定すると、第一引数のオプションが呼ばれたらTrueを返すようにできる。)
helpヘルプメッセージに説明を追加できる。

詳細は、こちらのページに記載あります。

 

例:引数に1つの文字列を設定する

/myapp/management/commands/testcommand.py
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "テストコマンド"

    def handle(self, *args, **options):
        if options['name']:
            self.__name(options['name'])

    def add_arguments(self, parser):
        parser.add_argument('--name', nargs='?', default='shiguregaki', type=str, help='名前入りで挨拶する')

    def __name(self, args):
        print("Hello "+args)

 

コマンド実行結果
$ python manage.py testcommand --name shigure
Hello shigure

$ python manage.py testcommand
Hello shiguregaki

$ python manage.py testcommand --greeting
Hello!
Hello shiguregaki

$ python manage.py testcommand --name 0
Hello 0

【エラーケース】
$ python manage.py testcommand --name shigure 0
manage.py testcommand: error: unrecognized arguments: 0

--nameオプションを指定しないと、default引数で指定したshiguiregakiで実行されています。
・が、--greetingオプションのみでも--nameオプションの処理が実行されています。default引数を指定することで意図しない動きにならないように注意が必要ですね。
--name 0と数値を指定してみましたが、文字列として読み取るようです。

 

例:引数に2つの数値を設定する

/myapp/management/commands/testcommand.py
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "テストコマンド"

    def handle(self, *args, **options):
        if options['greeting']:
            print("Hello!")
        if options['age']:
            self.__age(options['age'])

    def add_arguments(self, parser):
        parser.add_argument('--greeting', action='store_true', help='挨拶する')
        parser.add_argument('-a', '--age', nargs=2, type=int, help='年齢と誕生月入りで挨拶する')

    def __age(self, args):
        age = args[0]
        birth_month = args[1]
        print("Hello! I'm {0} years old. My Birthday month is {1}!".format(age, birth_month))

 

コマンド実行結果
$ python manage.py testcommand --age 20 4
Hello! I'm 20 years old. My Birthday month is 4!

$ python manage.py testcommand -a 25 5
Hello! I'm 25 years old. My Birthday month is 5!

$ python manage.py testcommand
(何も表示なし)

$ python manage.py testcommand --greeting
Hello!

【エラーケース】
$ python manage.py testcommand --age 20
manage.py testcommand: error: argument -a/--age: expected 2 arguments

$ python manage.py testcommand --age a b
manage.py testcommand: error: argument -a/--age: invalid int value: 'a'

--age-aの両方でオプションの実行ができています。
・今回は、defaultの指定をしていないため、オプションを指定しなかった場合は__age()が実行されていません。

 

 

以上!

コメント

タイトルとURLをコピーしました