Django のコマンドを作って定期的に実行する
なんてことはないが、ちょっとハマったのでメモ
Django でカスタムコマンドをつくる
単純なスクリプトならそのまま Python のスクリプトとしてつかえばいいとおもうけど、 Django のコマンドであること、またそれらのモデルとかと連携したり、まああと意味合い的にそっちにもたせたいとかあるとき、コマンドを自作できる。
日本語 1.0::アクションを自作する — Django v1.0 documentation
英語 1.5::Writing custom django-admin commands | Django documentation | Django
英語の最新版のほうがわかりやすい。
階層をつくる
リポジトリ/プロジェクト/アプリ/ とツリー構造があるとき、INSTALLED_APPS に通っているパスで management/commands をつくる。python なので __init__.py も忘れずに。
BaseCommand クラスを継承したクラスをつくる
例だと class Command(BaseCommand) としてつくってる。クラス変数にオプションやらなんやらを設定できる。引数は optparse ベースで現状 python 2.7 だから argparse なんじゃないのとおもったけど、PyPi にはそれっぽいライブラリをつくってくれている人はいる。
実際の処理をかく
自分の場合は def handle(self, *args, **options) は main と同じような扱いにして、あとは関数に分けた。logger とかそういうバッチ処理系のなんかはよしなにした。
動かす
ファイル名が実行コマンドになっている(クラス名ではない) ので python manage.py some_command で実行できる
pyhton manage.py some_command
crontab で動かす
定期的に実行するなら crontab はまあつかう。で、登録する。それはいいが、2つハマった。
実行ユーザーの登録
実運営的にどうするかはわからないけど、実行ユーザーを管理したい場合は crontab -u user -e で登録できる。だけど Ubuntu だと sudo する必用があったっぽくて、root 権限でしか動かせないかと思ってた。そんなことはなかった。
sudo crontab -u atas -e
でよい
virtualenv 環境
普段 Python 関係の開発だと virtualenv(pyvenv) は必須だと思うけど、シェルから簡単に叩ける virtualenvwrapper を使っていた。でも、cron の登録では workon など virtualenvwrapper のコマンドは叩けないよね、ってところで少しハマってた。なので直接 virtualenv を activate すればよい
. ~/.virtualenv/dev/bin/activate
つなげる
こうなる
# m, h, d, w, m, command * * * * * . /home/atas/.virtualenv/dev/bin/activate && cd /path/to/porject && python mamage.py some_command
もうちょっとちゃんとやるならシェルスクリプトにして
#!/bin/bash export DJANGO_SETTINGS_MODULE=mysite.settings . /path/to/virtualenv/dev/bin/activate cd /path/to/src python manage.py some_command
のほうがいいかもしれない
トラブルシューティング
Django のカスタムコマンドはつくれている前提で crontab での実行がどうなってるかがみえなかった
- 登録っぽいことをかく
- ログは /var/log/syslog に出るらしい
- crontab の設定自体は /var/spool/crontab/... にあるらしい(が、実ファイルを直接触ることは基本しないらしい)
- tmux でペインを分けてログを監視する tail -f /var/log/syslog
- ちなみに less で表示して shift f でも同じ事できるけど、圧倒的に普段は tail -f のほうがいい感じだった
- logger も動かしているのでそのファイルの状態も tail -f some.log してみる
- VM で開発しているのでたぶん大丈夫だけど時刻設定は date コマンドで確認する。1分を待つのが長い。
- ユーザーやなんやらでうごかなかったけど、結局はよく考えたら virtualenv 環境で環境のパスを実行ユーザーが見れないと困るよね*1、というようなところで解決
まあ
ある
*1:この記事をかいたときPYTHONPATH だと思ってたけど違った