Hoe te voorkomen dat de methode AppConfig.ready() twee keer wordt uitgevoerd in Django

Ik wil wat code uitvoeren bij het opstarten van de Django-server, maar ik wil dat het maar één keer wordt uitgevoerd. Wanneer ik momenteel de server start, wordt deze twee keer uitgevoerd. Documentatiezegt dat dit zou kunnen gebeuren en:

u moet een vlag op uw AppConfig-klassen plaatsen om te voorkomen dat ze opnieuw worden uitgevoerd
code die precies één keer moet worden uitgevoerd.

Enig idee hoe dit te bereiken? Afdrukopdracht hieronder wordt nog steeds twee keer uitgevoerd.

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

Antwoord 1, autoriteit 100%

Wanneer u python manage.py runservergebruikt, start Django twee processen, één voor de eigenlijke ontwikkelserver en andere om uw applicatie opnieuw te laden wanneer de code verandert.

Je kunt de server ook starten zonder de herlaadoptie, en je zult zien dat slechts één lopend proces maar één keer wordt uitgevoerd:

python manage.py runserver --noreload

Je kunt deze link zien, het lost de ready()methode op twee keer in Django


Antwoord 2, autoriteit 62%

als u --noreloadniet wilt gebruiken, kunt u:

vervang de regel in __init__.pyvan uw app die u gebruikt om de configuratie op te geven:

default_app_config = 'mydjangoapp.apps.MydjangoappConfig'

door dit:

import os
if os.environ.get('RUN_MAIN', None) != 'true':
    default_app_config = 'mydjangoapp.apps.MydjangoappConfig'

Antwoord 3, autoriteit 7%

U moet vergrendeling implementeren. Het is geen eenvoudig probleem en de oplossing zal niet natuurlijk aanvoelen omdat je te maken hebt met processen en threads. Wees gewaarschuwd, er zijn veel antwoorden op het probleem van vergrendeling, enkele eenvoudigere benaderingen:

Een bestandsvergrendeling: Zorg voor een enkele instantie van een toepassing in Linux(merk op dat threads standaard bestandsvergrendeling delen, dus dit antwoord moet worden uitgebreid om rekening te houden met threads).

Er is ook dit antwoord dat een Python-pakket gebruikt met de naam tendodat de implementatie van een bestandsvergrendeling omvat: https://stackoverflow.com/a/1265445/181907

Django zelf biedt een geabstraheerd draagbaar hulpprogramma voor het vergrendelen van bestanden in django.core.files.locks.


Antwoord 4, autoriteit 5%

Zoals Roberto al zei, moet u vergrendeling implementeren om dit te doen wanneer u uw server uitvoert via het runserver-commando, als u de standaard auto_reload-functionaliteit wilt gebruiken.

Django implementeert zijn auto_reload via threading en importeert dus de AppConfig in twee afzonderlijke threads, de hoofdthread ‘command/watch’ en de thread ‘reload’ waarop de server draait. Voeg een printopdracht toe aan de module en je zult dit in actie zien. De ‘main’-thread laadt de AppConfig-bestanden als onderdeel van de BaseCommand-uitvoering, en de ‘reload’-thread laadt ze vervolgens opnieuw tijdens het opstarten van de server.

Als je code hebt die niet in beide threads kan worden uitgevoerd, zijn je opties enigszins beperkt. Je kunt een thread lock implementeren zodat de ‘reload’ thread niet ready(); je kunt naar een productieomgeving gaan om je server te laten draaien (Gunicorn is bijvoorbeeld heel snel in te stellen, zelfs om te testen); of je kunt je methode op een andere manier aanroepen in plaats van ready() te gebruiken.

Ik zou aanraden om naar een geschikte omgeving te verhuizen, maar de beste optie hangt echt af van wat de methode die je belt precies moet doen.


Antwoord 5, autoriteit 5%

Ik ontdekte dat dit voor mij werkte zonderde vlag --noreloadte gebruiken in python manage.py runserver.

Controleer op een omgevingsvariabelein de ready()-methode. De env-variabele blijft niet bestaan ​​nadat de toepassing is beëindigd, maar blijft bestaan ​​als de codewijzigingen worden gedetecteerd door de server en nadat deze zichzelf automatisch opnieuw heeft geladen.

# 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

Antwoord 6

Er is vastgesteld dat AppConfig twee keer is gestart en ervoor heeft gezorgd dat de planner twee keer is gestart met deze configuratie. Instantieer in plaats daarvan de planner in urls.py zoals hieronder weergegeven:

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()

Other episodes