web-dev-qa-db-fra.com

Django - Configurer un travail planifié?

Je travaille sur une application Web utilisant Django et je suis curieux de savoir s'il existe un moyen de planifier l'exécution périodique d'un travail. 

En gros, je veux juste parcourir la base de données et faire des calculs/mises à jour de façon automatique et régulière, mais je ne trouve pas de documentation à ce sujet.

Est-ce que quelqu'un sait comment mettre cela en place?

Pour clarifier: je sais que je peux configurer un travail cron pour le faire, mais je suis curieux de savoir s'il existe une fonctionnalité dans Django qui fournit cette fonctionnalité. J'aimerais que les gens puissent déployer cette application eux-mêmes sans avoir à faire beaucoup de config (de préférence zéro).

J'ai envisagé de déclencher ces actions "rétroactivement" en vérifiant simplement si un travail aurait dû être exécuté depuis la dernière demande envoyée sur le site, mais j'espère un résultat un peu plus propre.

450
TM.

Une solution que j'ai employée est de faire ceci:

1) Créez une commande de gestion custom , par exemple. 

python manage.py my_cool_command

2) Utilisez cron (sous Linux) ou at (sous Windows) pour exécuter ma commande aux moments requis.

C'est une solution simple qui ne nécessite pas l'installation d'une pile AMQP lourde. Cependant, il y a de beaux avantages à utiliser quelque chose comme le céleri, mentionné dans les autres réponses. En particulier, avec Celery, il est agréable de ne pas avoir à répartir la logique de votre application dans des fichiers crontab. Cependant, la solution cron fonctionne assez bien pour une application de taille petite à moyenne et pour laquelle vous ne voulez pas beaucoup de dépendances externes.

MODIFIER:

Dans les versions ultérieures de Windows, la commande at est obsolète pour Windows 8, Server 2012 et versions ultérieures. Vous pouvez utiliser schtasks.exe pour le même usage.

325
Brian Neal

Celery est une file d’attente distribuée, construite sur AMQP (RabbitMQ). Il gère également les tâches périodiques de manière cronienne (voir tâches périodiques ). En fonction de votre application, cela pourrait valoir le coup d'oeil.

Le céleri est assez facile à configurer avec Django ( docs ), et des tâches périodiques ignoreront les tâches manquées en cas d’indisponibilité. Le céleri a également des mécanismes de nouvelle tentative intégrés, au cas où une tâche échouerait.

128
dln

Nous avons open-source ce que je pense est une application structurée. la solution de Brian ci-dessus y fait également allusion. J'aimerais tout/tous les commentaires!

https://github.com/tivix/Django-cron

Il vient avec une commande de gestion:

./manage.py runcrons

Cela fait le travail. Chaque cron est modélisé en tant que classe (donc tout est sur OO) et chaque cron fonctionne à une fréquence différente et nous nous assurons que le même type de cron ne fonctionne pas en parallèle (au cas où les crons prennent plus de temps que leur fréquence!)

Merci!

46
chachra

Si vous utilisez un système d’exploitation POSIX standard, vous utilisez cron .

Si vous utilisez Windows, vous utilisez at .

Ecrivez une commande de gestion Django à 

  1. Déterminez sur quelle plate-forme ils sont.

  2. Exécutez la commande "AT" appropriée pour vos utilisateurs, ou mettez à jour la crontab pour vos utilisateurs.

35
S.Lott

Nouvelle application Django connectable intéressante: Django-chronograph

Vous devez seulement ajouter une entrée cron qui agit comme une minuterie, et vous avez une très belle interface admin Django dans les scripts à exécuter.

23
Van Gale

Regardez Django Poor Man's Cron, une application Django qui utilise des robots spammeurs, des robots d'indexation des moteurs de recherche et autres pour exécuter des tâches planifiées à intervalles réguliers

Voir: http://code.google.com/p/Django-poormanscron/

14
user41767

RabbitMQ et Celery ont plus de fonctionnalités et de capacités de gestion de tâches que Cron. Si l’échec de la tâche n’est pas un problème et que vous pensez gérer des tâches interrompues lors du prochain appel, Cron suffit.

Celery & AMQP vous permettra de gérer la tâche interrompue et sera exécutée à nouveau par un autre ouvrier (les ouvriers de Celery écoutent la tâche suivante sur laquelle travailler) jusqu'à ce que l'attribut max_retries de la tâche soit atteint. Vous pouvez même appeler des tâches en cas d'échec, comme consigner l'échec ou envoyer un courrier électronique à l'administrateur une fois que le max_retries a été atteint.

Et vous pouvez distribuer des serveurs Celery et AMQP lorsque vous avez besoin de faire évoluer votre application.

9
Ravi Kumar

La suggestion de Brian Neal d'exécuter des commandes de gestion via cron fonctionne bien, mais si vous cherchez quelque chose d'un peu plus robuste (mais pas aussi élaboré que Celery), je chercherais dans une bibliothèque du type Kronos :

# app/cron.py

import kronos

@kronos.register('0 * * * *')
def task():
    pass
9
Johannes Gorset

Personnellement, j’utilise cron, mais le emploi du temps parties de Django-extensions semble intéressant.

8
Van Gale

Placez les éléments suivants en haut de votre fichier cron.py:

#!/usr/bin/python
import os, sys
sys.path.append('/path/to/') # the parent directory of the project
sys.path.append('/path/to/project') # these lines only needed if not on path
os.environ['Django_SETTINGS_MODULE'] = 'myproj.settings'

# imports and code below
6
Matt McCormick

J'avais exactement les mêmes exigences il y a quelque temps et j'ai fini par les résoudre en utilisant APScheduler ( Guide de l'utilisateur )

Il simplifie les tâches de planification et les maintient indépendantes de l'exécution de certains codes à partir de requêtes. Voici un exemple simple que j'ai utilisé dans mon code.

from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()
job = None

def tick():
    print('One tick!')\

def start_job():
    global job
    job = scheduler.add_job(tick, 'interval', seconds=3600)
    try:
        scheduler.start()
    except:
        pass

J'espère que cela aide quelqu'un!

6
PhoenixDev

Je viens de penser à cette solution plutôt simple:

  1. Définissez une fonction d'affichage do_work (req, param) comme vous le feriez avec n'importe quelle autre vue, avec un mappage d'URL, renvoyez un HttpResponse, etc.
  2. Configurez une tâche cron avec vos préférences de minuterie (ou en utilisant AT ou des tâches planifiées sous Windows) qui exécute curl http: // hôte local/votre/mappé/url? Param = value.

Vous pouvez ajouter des paramètres mais en ajoutant simplement des paramètres à l'URL.

Dites-moi ce que vous en pensez.

[Update] J'utilise maintenant la commande runjob de Django-extensions au lieu de curl.

Mon cron ressemble à quelque chose comme ça:

@hourly python /path/to/project/manage.py runjobs hourly

... et ainsi de suite pour tous les jours, tous les mois, etc. Vous pouvez également le configurer pour exécuter un travail spécifique.

Je le trouve plus facile à gérer et plus propre. Ne nécessite pas de mapper une URL sur une vue. Définissez simplement votre classe d'emploi et votre crontab et vous êtes prêt.

6
Michael

Bien que ne faisant pas partie de Django, Airflow est un projet plus récent (à partir de 2016) qui est utile pour la gestion des tâches.

Airflow est un système d'automatisation et de planification de flux de travail qui peut être utilisé pour créer et gérer des pipelines de données. Une interface Web fournit au développeur une gamme d’options pour la gestion et la visualisation de ces pipelines.

Airflow est écrit en Python et est construit en utilisant Flask.

Airflow a été créé par Maxime Beauchemin chez Airbnb et en source ouverte au printemps 2015. Il a rejoint le programme d'incubation de la Apache Software Foundation à l'hiver 2016. Voici la page du projet Git et quelques ajouts informations générales .

6
Alexander

après la partie de code, je peux écrire n'importe quoi comme mon view.py :)

#######################################
import os,sys
sys.path.append('/home/administrator/development/store')
os.environ['Django_SETTINGS_MODULE']='store.settings'
from Django.core.management impor setup_environ
from store import settings
setup_environ(settings)
#######################################

de http://www.cotellese.net/2007/09/27/running-external-scripts-against-Django-models/

4
xiaohei

Vous devez absolument consulter Django-q! Il ne nécessite aucune configuration supplémentaire et dispose probablement de tout le nécessaire pour gérer les problèmes de production des projets commerciaux.

Il est activement développé et s’intègre très bien avec Django, Django ORM, Mongo, Redis. Voici ma configuration:

# Django-q
# -------------------------------------------------------------------------
# See: http://Django-q.readthedocs.io/en/latest/configure.html
Q_CLUSTER = {
    # Match recommended settings from docs.
    'name': 'DjangoORM',
    'workers': 4,
    'queue_limit': 50,
    'bulk': 10,
    'orm': 'default',

# Custom Settings
# ---------------
# Limit the amount of successful tasks saved to Django.
'save_limit': 10000,

# See https://github.com/Koed00/Django-q/issues/110.
'catch_up': False,

# Number of seconds a worker can spend on a task before it's terminated.
'timeout': 60 * 5,

# Number of seconds a broker will wait for a cluster to finish a task before presenting it again. This needs to be
# longer than `timeout`, otherwise the same task will be processed multiple times.
'retry': 60 * 6,

# Whether to force all async() calls to be run with sync=True (making them synchronous).
'sync': False,

# Redirect worker exceptions directly to Sentry error reporter.
'error_reporter': {
    'sentry': RAVEN_CONFIG,
},
}
2
saran3h

J'ai eu quelque chose de similaire avec votre problème aujourd'hui.

Je ne voulais pas le faire gérer par le serveur via cron (et la plupart des bibliothèques étaient juste des aides cron à la fin).

J'ai donc créé un module de planification et l'a connecté au init.

Ce n'est pas la meilleure approche, mais cela m'aide à avoir tout le code en un seul endroit et à son exécution liée à l'application principale.

2
Fabricio Buzeto

Oui, la méthode ci-dessus est tellement géniale. Et j'ai essayé certains d'entre eux. Enfin, j'ai trouvé une méthode comme celle-ci:

    from threading import Timer

    def sync():

        do something...

        sync_timer = Timer(self.interval, sync, ())
        sync_timer.start()

Tout comme récursif .

Ok, j'espère que cette méthode peut répondre à vos besoins. :)

2
Ni Xiaoni

J'utilise le céleri pour créer mes tâches périodiques. Tout d'abord, vous devez l'installer comme suit:

pip install Django-celery

N'oubliez pas d'enregistrer Django-celery dans vos paramètres pour pouvoir faire quelque chose comme ceci:

from celery import task
from celery.decorators import periodic_task
from celery.task.schedules import crontab
from celery.utils.log import get_task_logger
@periodic_task(run_every=crontab(minute="0", hour="23"))
def do_every_midnight():
 #your code

Je ne suis pas sûr que cela sera utile à quiconque, car je devais fournir aux autres utilisateurs du système la planification des travaux, sans leur donner accès au planificateur de tâches du serveur (windows), j'ai créé cette application réutilisable.

Veuillez noter que les utilisateurs ont accès à un dossier partagé sur le serveur où ils peuvent créer le fichier de commande/tâche/.bat requis. Cette tâche peut ensuite être programmée à l'aide de cette application.

Le nom de l'application est Django_Windows_Scheduler

Capture d'écran:  enter image description here

1
just10minutes

Django Q est une solution plus moderne (par rapport au céleri): https://Django-q.readthedocs.io/en/latest/index.html

Il possède une excellente documentation et est facile à maîtriser. La prise en charge de Windows fait défaut, car Windows ne prend pas en charge la mise en fourrière de processus. Mais cela fonctionne bien si vous créez votre environnement de développement à l'aide du sous-système Windows pour Linux. 

1
devdrc

Si vous voulez quelque chose de plus fiable que Céleri _, essayez TaskHawk qui repose sur AWS SQS/SNS.

Voir: http://taskhawk.readthedocs.io

0
Sriram

Le moyen le plus simple est d'écrire une commande shell personnalisée, voir Documentation Django et de l'exécuter à l'aide d'un cronjob sur linux. Cependant, je recommanderais fortement d'utiliser un courtier de messages comme RabbitMQ associé au céleri. Peut-être pourriez-vous jeter un coup d’œil sur This Tutorial

0
Hamfri

Pour les projets simples dockerisés, je ne pouvais pas vraiment voir aucune réponse adaptée.

J'ai donc écrit une solution très dépourvue de bases, sans recourir à des bibliothèques externes ni à des déclencheurs, qui fonctionne toute seule. Aucun os-cron externe n'est nécessaire, il devrait fonctionner dans tous les environnements.

Cela fonctionne en ajoutant un middleware: middleware.py 

import threading

def should_run(name, seconds_interval):
    from application.models import CronJob
    from Django.utils.timezone import now

    try:
        c = CronJob.objects.get(name=name)
    except CronJob.DoesNotExist:
        CronJob(name=name, last_ran=now()).save()
        return True

    if (now() - c.last_ran).total_seconds() >= seconds_interval:
        c.last_ran = now()
        c.save()
        return True

    return False


class CronTask:
    def __init__(self, name, seconds_interval, function):
        self.name = name
        self.seconds_interval = seconds_interval
        self.function = function


def cron_worker(*_):
    if not should_run("main", 60):
        return

    # customize this part:
    from application.models import Event
    tasks = [
        CronTask("events", 60 * 30, Event.clean_stale_objects),
        # ...
    ]

    for task in tasks:
        if should_run(task.name, task.seconds_interval):
            task.function()


def cron_middleware(get_response):

    def middleware(request):
        response = get_response(request)
        threading.Thread(target=cron_worker).start()
        return response

    return middleware

models/cron.py:

from Django.db import models


class CronJob(models.Model):
    name = models.CharField(max_length=10, primary_key=True)
    last_ran = models.DateTimeField()

settings.py:

MIDDLEWARE = [
    ...
    'application.middleware.cron_middleware',
    ...
]
0
y-spreen