はじめに
こんにちは、エンジニアのkeitaMaxです。python初心者がDajngoを使ってみようと思って色々試しています。
今回はDjangoを使用している時に定期実行を作成していたのですが、なぜか2回実行されてしまうという問題にあたったのでそれの解決した時のことを備忘録として書こうと思います。
誤っていることを書いていたらコメントしてくれると嬉しいです。
やっていたこと
apscheduler
を使用して定期的に実行をしようとしていました。
apps.py
import os
from django.apps import AppConfig
class AppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'app'
def ready(self):
from .management.commands.example import start
start()
apps.pyを上記のようにして、example.pyは以下のように5秒ごとに"Hello World!"を出すようにしていました。
example.py
def test():
logger.info("Hello World!")
def start():
scheduler = BackgroundScheduler()
scheduler.add_job(test, 'interval', seconds=5)
scheduler.start()
これをdocker-compose.yml
で
backend:
container_name: backend
build:
context: ./docker
dockerfile: Dockerfile
command: python3 manage.py runserver 0.0.0.0:8000
こんな感じで起動していました。
原因
どうやらrunserver
はソースコードの変更を検知して自動リロードするため、実際にはプロセスが2回起動されてしまうらしいです。
- 最初のプロセスはコード監視専用の親プロセス
- その後、実際にアプリを動かす子プロセスが起動
そして、ready() はこの両方で呼び出されるため、start() が2回実行されてしまっているらしです。
解決策
apps.pyのready() を 子プロセスだけ実行するように以下のように修正しました。
apps.py
import os
from django.apps import AppConfig
class AppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'app'
def ready(self):
if os.environ.get('RUN_MAIN') == 'true': # 子プロセスだけ実行するように修正
from .management.commands.scrape import start
start()
これで2回実行されることは無くなりました。
おわりに
この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。
最後まで読んでいただきありがとうございました!
参考