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

时间:2015-11-19 21:23:14

标签: python django

我想在启动Django服务器时执行一些代码,但我希望它只运行一次。目前,当我启动服务器时,它执行了两次。 Documentation说这可能会发生,并且:

  

您应该在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")

6 个答案:

答案 0 :(得分:6)

使用python manage.py时,runserver Django启动两个进程,一个用于实际的开发服务器,另一个用于在代码更改时重新加载应用程序。

您也可以在没有重新加载选项的情况下启动服务器,并且您将看到只运行一个进程只执行一次:

Sub Test()

    AllocateNamedRange ThisWorkbook, "SomeName", "=VERSCHUIVING(archief!$A$2;0;0;1;AANTALARG(archief!$A$3:$Y$3))", "A1"

    'English version:
    'AllocateNamedRange ThisWorkbook, "SomeName", "=OFFSET(archief!$A$2,0,0,1,COUNTA(archief!$A$3:$Y$3))", "A1"

End Sub

Sub Test2()

    'Using some of your code to find the name.
    Dim rFoundRange As Range

    With ThisWorkbook.Worksheets("Sheet1").Range("MDM_MDM_Tool_List")
        Set rFoundRange = .Find(what:=Source_1_Criteria)

        'If Source_1_Criteria isn't found it will throw an error so need to check if it's found first.
        If Not rFoundRange Is Nothing Then
            AllocateNamedRange ThisWorkbook, rFoundRange.Offset(0, 2).Value, _
                "=VERSCHUIVING(archief!$A$2;0;0;1;AANTALARG(archief!$A$3:$Y$3))", "A1"
        End If

    End With

End Sub

'---------------------------------------------------------------------------------------
' Procedure : AllocateNamedRange
' Purpose   : Deletes the named range if it already exists and then recreates it.
'---------------------------------------------------------------------------------------
Public Sub AllocateNamedRange(Book As Workbook, sName As String, sRefersTo As String, Optional ReferType = "R1C1")
    With Book
        If NamedRangeExists(Book, sName) Then .Names(sName).Delete
            If ReferType = "R1C1" Then
                .Names.Add Name:=sName, RefersToR1C1:=sRefersTo
        ElseIf ReferType = "A1" Then
                .Names.Add Name:=sName, RefersTo:=sRefersTo
        End If
    End With
End Sub

'---------------------------------------------------------------------------------------
' Procedure : NamedRangeExists
' Purpose   : Checks if a named range exists.  Returns TRUE or FALSE.
'---------------------------------------------------------------------------------------
Public Function NamedRangeExists(Book As Workbook, sName As String) As Boolean
    On Error Resume Next
        NamedRangeExists = Book.Names(sName).Index <> (Err.Number = 0)
    On Error GoTo 0
End Function

You can see this links,it resolve that ready() method running twice in Django

答案 1 :(得分:4)

正如Roberto所说,如果你想使用默认的auto_reload功能,你需要实现锁定,以便在通过runserver命令运行服务器时执行此操作。

Django通过线程实现了它的auto_reload,因此在两个独立的线程中导入AppConfig,主要的'command / watch'线程和运行服务器的'reload'线程。向模块添加打印语句,您将看到这一点。 'main'线程加载AppConfig文件作为其BaseCommand执行的一部分,然后'reload'线程在它启动服务器期间再次加载它们。

如果您的代码无法在这两个线程中运行,那么您的选项会受到一些限制。您可以实现线程锁定,以便'reload'线程不会运行ready();您可以移动到生产环境来运行您的服务器(例如,Gunicorn可以非常快速地进行设置,甚至可以进行测试);或者你可以用另一种方式调用你的方法,而不是使用ready()。

我建议移动到适当的环境,但最好的选择实际上取决于您调用的方法究竟应该做什么。

答案 2 :(得分:3)

您需要实现锁定。这不是一个简单的问题,当您处理进程和线程时,解决方案不会感觉自然。请注意,锁定问题有许多答案,一些更简单的方法:

文件锁定:Ensure a single instance of an application in Linux(请注意,默认情况下线程共享文件锁定,因此需要扩展此答案以考虑线程)。

还有这个答案使用名为tendo的Python包来封装文件锁实现:https://stackoverflow.com/a/1265445/181907

Django本身在django.core.files.locks中提供了抽象的可移植文件锁定实用程序。

答案 3 :(得分:2)

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

答案 4 :(得分:0)

我发现这对我有用没有--noreload 中使用 python manage.py runserver 标志。

检查 ready() 方法中的环境变量。 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
  

答案 5 :(得分:-1)

尝试使用实例变量,而不是类变量。例如:

class MyAppConfig(AppConfig):
    run_already = False

    def ready(self):
        if not self.run_already:
            print('Hello, world!')
            self.run_already = True