256 votes

Exécuter le code au démarrage de Django UNE SEULE FOIS ?

J'écris une classe Django Middleware que je veux exécuter une seule fois au démarrage, pour initialiser un autre code arbitraire. J'ai suivi la très belle solution postée par sdolan. aquí mais le message "Hello" est envoyé au terminal. deux fois . Par exemple

from django.core.exceptions import MiddlewareNotUsed
from django.conf import settings

class StartupMiddleware(object):
    def __init__(self):
        print "Hello world"
        raise MiddlewareNotUsed('Startup complete')

et dans mon fichier de configuration Django, j'ai inclus la classe dans la section MIDDLEWARE_CLASSES liste.

Mais lorsque j'exécute Django en utilisant runserver et que je demande une page, j'obtiens dans le terminal

Django version 1.3, using settings 'config.server'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Hello world
[22/Jul/2011 15:54:36] "GET / HTTP/1.1" 200 698
Hello world
[22/Jul/2011 15:54:36] "GET /static/css/base.css HTTP/1.1" 200 0

Avez-vous une idée de la raison pour laquelle "Hello world" est imprimé deux fois ? Merci.

1 votes

Juste par curiosité, avez-vous compris pourquoi le code dans init .py est exécuté deux fois ?

12 votes

@Mutant il n'est exécuté que deux fois sous runserver ... c'est parce que runserver charge d'abord les applications pour les inspecter et ensuite démarre réellement le serveur. Même lors du chargement automatique de runserver, le code n'est exécuté qu'une fois.

4 votes

Wow j'ai été ici.... donc merci encore pour le commentaire @Pykler, c'est ce que je me demandais.

353voto

Pykler Points 2698

Mise à jour : Django 1.7 dispose désormais d'une fonction crochet pour ce

fichier : myapp/apps.py

from django.apps import AppConfig
class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = "My Application"
    def ready(self):
        pass # startup code here

fichier : myapp/__init__.py

default_app_config = 'myapp.apps.MyAppConfig'

Pour Django < 1.7

La réponse numéro un ne semble plus fonctionner, urls.py est chargé à la première demande.

Ce qui a fonctionné dernièrement, c'est de placer le code de démarrage dans n'importe lequel de vos INSTALLED_APPS. init .py par exemple. myapp/__init__.py

def startup():
    pass # load a big thing

startup()

Lorsque vous utilisez ./manage.py runserver ... ceci est exécuté deux fois, mais c'est parce que runserver a quelques astuces pour valider les modèles d'abord etc ... les déploiements normaux ou même lorsque runserver se recharge automatiquement, ceci n'est exécuté qu'une fois.

0 votes

Dans la dernière version de django, cela ne s'exécute qu'une fois avec les commandes manage.py. Je ne suis même pas tout à fait sûr si c'est à cause de la nouvelle version de django ou quoi, mais en gros, même quand il s'exécute deux fois, la première fois était juste une vérification, la deuxième fois est celle où ça compte.

6 votes

Je pense que cela est exécuté pour chaque processus qui charge le projet. Donc, je ne vois pas pourquoi cela ne fonctionnerait pas parfaitement dans n'importe quel scénario de déploiement. Cela fonctionne pour les commandes de gestion. +1

0 votes

Note cependant ... Sous les serveurs wsgi (au moins uwsgi), il y a un chargement paresseux de l'application, ce qui signifie que cela sera déclenché à la première demande. En plaçant un appel à l'import myapp int he wsgi.py est ce que j'ai fini par faire, mais cela ne fonctionne pas toujours ! il échoue si vous démarrez des choses comme des sous-processus avec des pipes (ouais mortellement juste) qui ne peuvent pas être partagés avec d'autres processus (puisque uwsgi fork le processus original dans un modèle multi-travailleur.

147voto

S.Lott Points 207588

Mise à jour de la réponse de Pykler ci-dessous : Django 1.7 dispose désormais d'un crochet pour ce


Ne le faites pas de cette façon.

Vous ne voulez pas d'un "intergiciel" pour un démarrage unique.

Vous voulez exécuter le code dans le niveau supérieur urls.py . Ce module est importé et exécuté une fois.

urls.py

from django.confs.urls.defaults import *
from my_app import one_time_startup

urlpatterns = ...

one_time_startup()

0 votes

Ok, je vais essayer. Merci pour votre réponse rapide :)

0 votes

Juste pour être sûr, cela fonctionnera-t-il avec Django au-dessus de apache/mod_wsgi ? Ou juste avec la commande django runsrever ?

0 votes

@Michael : Ils sont effectivement identiques (sauf pour la vitesse et la "sécurité" globale). Oui. Il fonctionne sous Apache et mod_wsgi.

47voto

Augusto Men Points 1731

Cette question trouve une bonne réponse dans le billet de blog Crochet de point d'entrée pour les projets Django qui fonctionnera pour Django >= 1.4.

En gros, vous pouvez utiliser <project>/wsgi.py pour le faire, et il ne sera exécuté qu'une seule fois, au démarrage du serveur, mais pas lorsque vous exécutez des commandes ou importez un module particulier.

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")

# Run startup code!
....

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

0 votes

Encore une fois, ajoutez un commentaire pour confirmer que cette méthode n'exécutera le code qu'une seule fois. Aucun mécanisme de verrouillage n'est nécessaire.

0 votes

Scripts ont été ajoutés ici semblent ne pas être exécutés lorsque le cadre de test démarre.

0 votes

Cette réponse a mis fin à deux jours et demi de recherche de solutions qui n'ont tout simplement pas fonctionné.

32voto

Alberto Pianon Points 179

Comme suggéré par @Pykler, dans Django 1.7+ vous devriez utiliser le crochet expliqué dans sa réponse, mais si vous voulez que votre fonction n'est appelée que lorsque le serveur d'exécution est appelé. (et non lors de la réalisation de migrations, migrate, shell, etc. sont appelés), et vous voulez éviter les exceptions AppRegistryNotReady vous devez faire comme suit :

fichier : myapp/apps.py

import sys
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        if 'runserver' not in sys.argv:
            return True
        # you must import your modules here 
        # to avoid AppRegistryNotReady exception 
        from .models import MyModel 
        # startup code here

22voto

AnaPana Points 1220

Si cela aide quelqu'un, en plus de pykler's réponse, l'option "--noreload" empêche runserver d'exécuter deux fois la commande au démarrage :

python manage.py runserver --noreload

Mais cette commande ne rechargera pas runserver après les modifications d'autres codes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X