| この記事の要点 |
|
1. バッチ本体を管理コマンドにする
スクリプトを単独の .py にすると Django の設定読み込みを自分で書く必要があります。管理コマンドにすれば manage.py が全部やってくれます。
myapp/
management/
__init__.py
commands/
__init__.py
cleanup_sessions.py
# myapp/management/commands/cleanup_sessions.py
import logging
from django.core.management.base import BaseCommand
from django.utils import timezone
from myapp.models import Session
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = "期限切れのセッションを削除する"
def add_arguments(self, parser):
parser.add_argument("--days", type=int, default=30)
parser.add_argument("--dry-run", action="store_true")
def handle(self, *args, **options):
limit = timezone.now() - timezone.timedelta(days=options["days"])
qs = Session.objects.filter(expired_at__lt=limit)
count = qs.count()
if options["dry_run"]:
self.stdout.write(f"[DRY] {count} 件が対象です")
return
qs.delete()
logger.info("期限切れセッションを %d 件削除しました", count)
self.stdout.write(self.style.SUCCESS(f"{count} 件削除しました"))
python manage.py cleanup_sessions --days=60 --dry-run # まず動作確認
作り方の詳細は cron用コマンド・ファイルの作成 を参照してください。ここからは cron 側の設定を扱います。
2. crontab の書式
┌── 分 (0-59)
│ ┌── 時 (0-23)
│ │ ┌── 日 (1-31)
│ │ │ ┌── 月 (1-12)
│ │ │ │ ┌── 曜日 (0-7 0と7が日曜)
│ │ │ │ │
* * * * * 実行するコマンド
| 書き方 | 意味 |
|---|---|
0 3 * * * | 毎日 3:00 |
*/10 * * * * | 10 分おき |
0 9 * * 1-5 | 平日の 9:00 |
0 0 1 * * | 毎月 1 日の 0:00 |
0 2 * * 0 | 毎週日曜の 2:00 |
3. 登録する
crontab -e # 編集
crontab -l # 一覧
crontab -u www-data -l # 別ユーザーの分を見る
SHELL=/bin/bash
MAILTO=""
DJANGO_SETTINGS_MODULE=config.settings.production
# 毎日 3:00 に期限切れセッションを削除
0 3 * * * cd /srv/myproject && /srv/myproject/.venv/bin/python manage.py cleanup_sessions --days=30 >> /var/log/myproject/cleanup.log 2>&1
ポイントは 3 つです。
- 仮想環境の python を絶対パスで指定 —
source activateは要りません。.venv/bin/pythonを直接呼べば、その環境のライブラリが使われます cdでプロジェクトルートへ移動 —manage.pyと相対パスの設定ファイルを見つけるため- 出力をログへリダイレクト —
2>&1を付けてエラーも同じファイルに落とす
4. 動かないときに見るところ
| 症状 | 原因 |
|---|---|
| 何も起きない | cron の PATH は /usr/bin:/bin だけ。すべて絶対パスで書く |
ModuleNotFoundError: No module named 'django' | システムの python が使われている。.venv/bin/python を指定する |
ImproperlyConfigured: settings are not configured | DJANGO_SETTINGS_MODULE が渡っていない |
.env の値が読めない | cron はログインシェルを通らない。python-dotenv で明示的に読む |
| ログが空 | 書き込み先の権限。cron を動かすユーザーで書けるか確認する |
| 時刻がずれる | サーバーのタイムゾーンは UTC のことがある。timedatectl で確認 |
# cron が起動したかどうかはシステムログで分かる
sudo grep CRON /var/log/syslog | tail -20 # Debian / Ubuntu
sudo journalctl -u cron --since "1 hour ago"
# 環境の違いを再現して手で試す
env -i /srv/myproject/.venv/bin/python /srv/myproject/manage.py cleanup_sessions
最後のコマンドは環境変数を空にして実行します。手元で動くのに cron で失敗する場合、これで同じ失敗を再現できます。
5. 多重起動を防ぐ
*/5 * * * * /usr/bin/flock -n /tmp/import.lock -c "cd /srv/myproject && /srv/myproject/.venv/bin/python manage.py import_data" >> /var/log/myproject/import.log 2>&1
5 分おきの処理が 6 分かかると、前回が終わる前に次が始まります。flock -n を付けるとロックが取れないときは何もせず終了するため、二重実行によるデータ破壊を防げます。
6. systemd timer という選択肢
# /etc/systemd/system/cleanup.service
[Unit]
Description=Django cleanup batch
[Service]
Type=oneshot
User=www-data
WorkingDirectory=/srv/myproject
Environment=DJANGO_SETTINGS_MODULE=config.settings.production
ExecStart=/srv/myproject/.venv/bin/python manage.py cleanup_sessions
# /etc/systemd/system/cleanup.timer
[Unit]
Description=Run cleanup daily
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now cleanup.timer
systemctl list-timers cleanup.timer # 次回実行時刻を確認
journalctl -u cleanup.service -n 50 # ログはこちらに出る
cron に比べてログが journald に集約され、実行結果とかかった時間が残るのが利点です。Persistent=true を付けると、サーバーが停止していて実行できなかった分を起動後に補います。
関連
- django — 親カテゴリ
- cron用コマンド・ファイルの作成 — 管理コマンドの作り方
- ログ出力方法
- ログのローテート設定
- 本番環境へのインストールとアプリのデプロイ(nginx編)
- settings.pyの定数にアクセスする方法
子ページ
子ページはありません
同階層のページ
- 環境構築とプロジェクト/アプリの作成
- MVC(MVT)のそれぞれの使い方と説明
- データベースへの接続と操作
- Django Administration
- git管理
- エラー一覧
- バージョンの確認方法
- ログ出力方法
- SQLのログ出力方法
- ログのローテート設定
- settings.pyの定数にアクセスする方法
- 本番環境へのインストールとアプリのデプロイ(apache編)
- 本番環境へのインストールとアプリのデプロイ(nginx編)
- djangoアプリの本番の開始URLを変更する
- 静的(static)ファイルの置き場所と読み込み(画像、css、js )
- CSRFトークンをAjaxで使用する方法
- ajaxの使用例(POST編)
- ファイルのアップロードとファイルの名前
- クイックスタート/チュートリアル
- ログイン機能
- テンプレート側のログイン判定
- ビュー側のログイン判定
- 管理者ユーザーの作成/判定と管理画面
- モデルのjson化とレスポンス
- runserverでポートを指定する方法
- cronによるバッチ実行
- テンプレートで利用する共通のcontextを定義する方法
- プログラムが本番サーバーで反映されない場合の対処法
- APIの作成
- cron用コマンド・ファイルの作成
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38