如何避免AppConfig.ready()方法在Django中运行两次

5fjcxozz  于 2023-02-14  发布在  Go
关注(0)|答案(6)|浏览(393)

我想在Django服务器启动时执行一些代码,但我希望它只运行一次。目前,当我启动服务器时,它会执行两次。Documentation表示这种情况可能会发生,并且:
您应该在AppConfig类上放置一个标志,以防止重新运行应该只执行一次的代码。
知道如何实现吗?下面的Print语句仍然执行了两次。

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")
wtlkbnrh

wtlkbnrh1#

使用python manage.py runserver时,Django会启动两个进程,一个用于实际的开发服务器,另一个用于在代码更改时重新加载应用程序。
您可以在不使用reload选项的情况下启动服务器,并且您将只看到一个进程正在运行:

python manage.py runserver --noreload

另请参见the ready() method running twice in Django

watbbzwu

watbbzwu2#

如果不想使用--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'

或者,检查AppConfigready方法中的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')
inb24sb2

inb24sb23#

我发现这对我来说很有效没有使用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
83qze16e

83qze16e4#

你需要实现锁。这不是一个简单的问题,当你处理进程和线程时,解决方案会让你感觉不自然。注意,锁的问题有很多答案,一些简单的方法:
文件锁定:确保Linux中的应用程序只有一个示例(请注意,线程默认情况下共享文件锁,因此需要扩展此答案以考虑线程)。
也有这样的答案,它使用了一个名为tendo的Python包,该包封装了一个文件锁实现:https://stackoverflow.com/a/1265445/181907
Django本身在django.core.files.locks中提供了一个抽象的可移植文件锁定实用程序。

5ktev3wc

5ktev3wc5#

正如Roberto提到的,如果您想使用默认的auto_reload功能,那么在通过runserver命令运行服务器时,您需要实现锁定来实现这一点。
Django通过线程实现了auto_reload,因此在两个独立的线程中导入AppConfig,主'command/watch'线程和运行服务器的'reload'线程。在模块中添加一个print语句,您将看到这一操作。'main'线程在BaseCommand执行过程中加载AppConfig文件,'reload'线程在服务器启动时再次加载它们。
如果你的代码不能同时在这两个线程中运行,那么你的选择就受到了一定的限制。你可以实现一个线程锁,这样“reload”线程就不会运行ready();您可以转移到生产环境来运行您的服务器(例如Gunicorn的安装速度非常快,甚至可以用于测试);也可以用其他方法调用方法,而不用ready()。
我建议迁移到适当的环境中,但最佳选择实际上取决于您所调用的方法应该做什么。

6gpjuf90

6gpjuf906#

发现AppConfig被激发了两次,导致计划程序使用此配置启动了两次。请改为在www.example.com中示例化计划程序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()

相关问题