Tengo una función que debe ejecutarse en segundo plano en una de mis aplicaciones web.
Implementé una AppConfig personalizada como se muestra a continuación:
class MyAppConfig(AppConfig): run_already = False def ready(self): from .tasks import update_products if "manage.py" not in sys.argv and not self.run_already: self.run_already = True update_products()Sin embargo, este comando se ejecuta dos veces (la llamada update_products())
Como se indica en la documentación:
En el proceso de inicialización habitual, Django solo llama una vez al método ready. Pero en algunos casos de esquina, particularmente en las pruebas que están jugando con las aplicaciones instaladas, se puede llamar a ready más de una vez. En ese caso, escriba métodos idempotentes o coloque un indicador en sus clases AppConfig para evitar volver a ejecutar el código que debe ejecutarse exactamente una vez.
Siento que estoy siguiendo lo que dice la documentación. ¿Lo que da?
Como se indica en esta respuesta , si está ejecutando su aplicación, utilizando el comando python manage.py runserver en Django, su aplicación se ejecutará dos veces: una vez para validar sus modelos y la otra para ejecutar su aplicación.
Puede cambiar esto pasando la opción --noreload al comando runserver.
En heroku, gunicorn se inicia con más de un trabajador gunicorn. Establezca WEB_CONCURRENCY en 1 :
heroku config:set WEB_CONCURRENCY=1(ver Configuración básica )
Ninguna bandera funciona a nivel de clase. Django se ejecuta dos veces en dos procesos separados. Las variables de nivel de clase en dos procesos separados no son visibles entre sí. Use un indicador de una tabla de base de datos como en este código (SchedulerUtils es una clase escrita por mí con un método go() que inicia un programador de apscheduler de fondo. El modelo usa una fila en la tabla scheduler_schedulerinfo, por lo que debe insertar esta fila antes: "INSERTAR EN los valores de Scheduler_schedulerinfo (iniciados) (0);"):
################################## APPS.PY import os from django.apps import AppConfig from apscheduler.schedulers.background import BlockingScheduler, BackgroundScheduler from scheduler.utils import SchedulerUtils class SchedulerConfig(AppConfig): name = 'scheduler' def ready(self): startScheduler = True pid = os.getpid() #check i'm on heroku if (os.environ.get("DYNO")): # i'm on heroku, here runs twice print("[%s] DYNO ENV exists, i'm on heroku" % pid) from scheduler.models import SchedulerInfo schedInfo = SchedulerInfo.objects.all().first() if (schedInfo.started == 0): print("[%s] Scheduler not started, starting.... " % pid) startScheduler = True # set flag to 1 SchedulerInfo.objects.all().update(started = 1) else: print("[%s] Scheduler already running, not starting." % pid) startScheduler = False # already running # reset to 0 for next time SchedulerInfo.objects.all().update(started = 0) # PRINT FLAG VALUE from scheduler.models import SchedulerInfo schedInfo = SchedulerInfo.objects.all().first() print("[%s] Value of flag schedulerinfo.started: %d" % (pid, schedInfo.started)) if (startScheduler): su = SchedulerUtils() su.go() ##################################### MODELS.PY from django.db import models class SchedulerInfo(models.Model): started = models.IntegerField(default=0)