我想在Django服务器启动时执行一些代码,但我希望它只运行一次。目前,当我启动服务器时,它会执行两次。文件表明这可能会发生,并且:
应该在AppConfig类上设置一个标志,以防止重新运行应该只执行一次的代码。
知道如何做到这一点吗?下面的打印语句仍然执行两次。
from django.apps import AppConfig
import app.mqtt
from apscheduler.schedulers.background import BackgroundScheduler
class MyAppConfig(AppConfig):
name = 'app'
verbose_name = "HomeIoT"
run_already = False
def ready(self):
if MyAppConfig.run_already: return
MyAppConfig.run_already = True
print("Hello")
使用python manage.py runserver
时,Django启动两个进程,一个用于实际的开发服务器,另一个用于在代码更改时重新加载应用程序。
您可以在没有重新加载选项的情况下启动服务器,并且您将只看到一个进程在运行:
python manage.py runserver --noreload
另请参阅在Django中运行两次的ready()
方法。
如果您不想使用--noreload
,您可以:
替换应用程序的__init__.py
中用于指定配置的行:
default_app_config = 'mydjangoapp.apps.MydjangoappConfig'
通过这个:
import os
if os.environ.get('RUN_MAIN', None) != 'true':
default_app_config = 'mydjangoapp.apps.MydjangoappConfig'
或者,在AppConfig
ready
方法中检查RUN_MAIN
环境变量:
import os
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'app'
def ready(self):
if os.environ.get('RUN_MAIN'):
print('Hello')
我发现在没有使用python manage.py runserver
中的--noreload
标志的情况下,这对我很有效。
检查ready()
方法中的环境变量。env变量在应用程序结束后不会持久存在,但如果服务器检测到代码更改并自动重新加载后,env变量会持久存在。
# File located in mysite/apps.py
from django.apps import AppConfig
import os
class CommandLineRunner(AppConfig):
name = 'mysite'
def ready(self):
run_once = os.environ.get('CMDLINERUNNER_RUN_ONCE')
if run_once is not None:
return
os.environ['CMDLINERUNNER_RUN_ONCE'] = 'True'
# The code you want to run ONCE here
您需要实现锁定。这不是一个简单的问题,在处理进程和线程时,解决方案也不会很自然。请注意,锁定问题有很多答案,一些更简单的方法:
文件锁:确保Linux中的应用程序只有一个实例(请注意,默认情况下线程共享文件锁,因此需要扩展此答案以考虑线程)。
还有一个答案,它使用了一个名为tendo
的Python包,该包封装了一个文件锁实现:https://stackoverflow.com/a/1265445/181907
Django本身在django.core.files.locks
中提供了一个抽象的可移植文件锁定实用程序。
正如Roberto所提到的,如果您想使用默认的auto_reload功能,那么在通过runserver命令运行服务器时,您需要实现锁定。
Django通过线程实现了它的auto_reload,因此在两个独立的线程中导入AppConfig,即运行服务器的主"command/watch"线程和"reload"线程。将打印语句添加到模块中,您将看到它的实际操作。"main"线程加载AppConfig文件作为其BaseCommand执行的一部分,然后"reload"线程在启动服务器期间再次加载这些文件。
如果您的代码不能同时在这两个线程中运行,那么您的选择会受到一定的限制。你可以实现一个线程锁,这样'reload'线程就不会运行ready();你可以转移到生产环境来运行你的服务器(例如,Gunicorn的设置非常快,甚至是测试);或者,您可以用另一种方式调用方法,而不是使用ready()。
我建议转移到一个合适的环境中,但最好的选择实际上取决于您调用的方法应该做什么。
发现AppConfig被激发了两次,并导致调度程序使用此配置启动了两次。相反,在urls.py中实例化调度程序,如下所示:
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/login/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/v1/login/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
path('api/v1/', include('rest_registration.api.urls'))
]
scheduler = BackgroundScheduler()
scheduler.add_job(task.run, trigger='cron', hour=settings.TASK_RUNNER_HOURS, minute=settings.TASK_RUNNER_MINUTES, max_instances=1)
scheduler.start()