我想在启动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")
答案 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