web-dev-qa-db-fra.com

Où les gestionnaires de signaux doivent-ils vivre dans un projet Django?

Je viens de commencer à implémenter des écouteurs de signaux dans un projet Django. Bien que je comprenne ce qu'ils sont et comment les utiliser. J'ai du mal à trouver où les mettre. La documentation de le site Django a ceci à dire:

Où devrait vivre ce code?

Vous pouvez mettre le code d'enregistrement et de gestion des signaux où vous le souhaitez. Cependant, vous devrez vous assurer que le module dans lequel il se trouve est importé très tôt afin que la gestion du signal soit enregistrée avant d'envoyer des signaux. Cela fait de models.py de votre application un bon endroit pour mettre l'enregistrement des gestionnaires de signaux.

Bien que ce soit une bonne suggestion, avoir des classes ou des méthodes non modèles dans mes models.py me frotte juste dans le mauvais sens.

Alors, quelle est la meilleure pratique/règle pour le stockage et l'enregistrement des gestionnaires de signaux?

131
Jason Webb

J'aime réellement leur faire des méthodes de classe du modèle lui-même. Cela garde tout dans une même classe et signifie que vous n'avez pas à vous soucier d'importer quoi que ce soit.

39
Daniel Roseman

Cela a été ajouté à la documentation lorsque Django 1.7 a été publié:

À strictement parler, la gestion du signal et le code d'enregistrement peuvent vivre où vous le souhaitez, bien qu'il soit recommandé d'éviter le module racine de l'application et son module de modèles pour minimiser les effets secondaires de l'importation de code.

En pratique, les gestionnaires de signaux sont généralement définis dans un sous-module de signaux de l'application à laquelle ils se rapportent. Les récepteurs de signaux sont connectés dans la méthode ready () de votre classe de configuration d'application. Si vous utilisez le décorateur receiver (), importez simplement le sous-module de signaux dans ready ().

Modifié dans Django 1.7: Étant donné que ready () n'existait pas dans les versions précédentes de Django, l'enregistrement du signal se produisait généralement dans le module modèles.

La meilleure pratique consiste à définir vos gestionnaires dans handlers.py dans un sous-module de signaux, par ex. un fichier qui ressemble à:

yourapp/signaux/handlers.py:

from Django.db.models.signals import pre_save
from Django.dispatch import receiver
from myapp.models import MyModel

@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    pass

Le meilleur endroit pour enregistrer votre gestionnaire de signaux est alors dans l'AppConfig de l'application qui le définit, en utilisant la méthode ready () . Cela ressemblera à ceci:

yourapp/apps.py:

from Django.apps import AppConfig

class TasksConfig(AppConfig):
    name = 'tasks'
    verbose_name = "Tasks"

    def ready(self):
        import yourproject.yourapp.signals.handlers #noqa

Assurez-vous que vous chargez votre AppConfig en le spécifiant soit directement dans vos settings.py INSTALLED_APPS, soit dans le __init__ de votre application. Voir voir la documentation ready () pour plus d'informations.

Remarque: Si vous fournissez également des signaux à d'autres applications, mettez-les dans le __init__ dans votre module de signaux, par ex. un fichier qui ressemble à:

yourapp/signaux/__ init __. py

import Django.dispatch

task_generate_pre_save = Django.dispatch.Signal(providing_args=["task"])

Une autre application peut ensuite écouter votre signal en l'important et en l'enregistrant, par ex. from yourapp.signals import task_generate_pre_save. Séparer vos signaux de vos gestionnaires permet de garder les choses propres.

Instructions pour Django 1.6:

Si vous êtes toujours bloqué sur Django 1.6 ou inférieur, alors vous feriez la même chose (définissez vos gestionnaires dans yourapp/signaux/handlers.py) mais plutôt que d'utiliser AppConfig, vous feriez charger les gestionnaires via le __init__.py de votre application, par exemple quelque chose comme:

yourapp/__ init __. py

import signals

Ce n'est pas aussi agréable que d'utiliser la méthode ready () car cela provoque souvent des problèmes d'importation circulaire.

225
Aidan

Je viens juste de découvrir cela, et comme mes signaux ne sont pas liés au modèle, j'ai pensé ajouter ma solution.

Je connecte diverses données autour de la connexion/déconnexion et je devais me connecter à Django.contrib.auth.signals.

J'ai mis les gestionnaires de signaux dans un signals.py, puis importé des signaux du __init__.py fichier de module, car je crois qu'il est appelé dès le démarrage de l'application (le test avec une instruction print suggère qu'il est appelé avant même la lecture du fichier de paramètres.)

# /project/__init__.py
import signals

et dans signaux.py

# /project/signals.py
from Django.contrib.auth.signals import user_logged_in

def on_logged_in(sender, user, request, **kwargs):
    print 'User logged in as: \'{0}\''.format(user)

user_logged_in.connect(on_logged_in)

Je suis assez nouveau pour Django (/ python) donc je suis ouvert à tous ceux qui me disent que c'est une terrible idée!

39
Hugo Rodger-Brown

J'ai récemment lu this article sur les meilleures pratiques en matière de mise en page de vos projets/applications, et il suggère que tous vos signaux de répartiteur personnalisés devraient aller dans un fichier appelé signals.py. Cependant, cela ne résout pas complètement votre problème, car vous devez toujours les importer quelque part, et plus tôt ils seront importés, mieux ce sera.

La suggestion de modèle est bonne. Puisque vous avez déjà tout défini dans votre signals.py fichier, il ne doit pas prendre plus d'une ligne en haut du fichier. Ceci est similaire à la façon dont le admin.py le fichier est disposé (avec les définitions de classe en haut et le code pour enregistrer toutes les classes d'administration personnalisées en bas), si vous définissez vos signaux, puis connectez-les dans le même fichier.

J'espère que ça t'as aidé! En fin de compte, cela revient à ce que vous préférez.

13
hora

models.py et signaux.py dans chaque application ont été les endroits recommandés pour connecter des signaux, cependant, ils ne sont pas la meilleure solution, à mon avis, pour garder les signaux et les gestionnaires envoyés. L'envoi devrait être la raison pour laquelle les signaux et les gestionnaires ont été inventés à Django.

Je luttais depuis longtemps et finalement nous avons trouvé la solution.

créer un module de connecteur dans le dossier de l'application

nous avons donc:

app/
    __init__.py
    signals.py
    models.py
    connectors.py

dans app/connecteurs.py, nous avons défini des gestionnaires de signaux et les avons connectés. Un exemple est fourni:

from signals import example_signal
from models import ExampleModel
from Django.db.models.signals import post_save, post_delete

def hanndler(sender, *args, **kwargs):
    pass

post_save.connect(hander, sender=ExampleModel)

puis dans models.py, nous ajoutons la ligne suivante à la fin du fichier:

from app import connector

Tout est fait ici.

De cette façon, nous pouvons mettre des signaux dans signaux.py et tous les gestionnaires dans connecteurs.py. Pas de dégâts dans les modèles et les signaux.

J'espère que cela fournit une autre solution.

7
samuel

Petit rappel sur AppConfig. N'oubliez pas de régler:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
3
valex

Je les garde dans un fichier séparé signals.py , Dans models.py une fois tous les modèles définis. Je les importe et connecte des modèles aux signaux.

signaux.py

#  necessary imports

def send_mail_on_save(<args>):
    # code here 

models.py

# imports
class mymodel(models.Model):
    # model here

# import signals
from signals import send_mail_on_save
# connect them 
post_save.connect(send_mail_on_save,sender=mymodel)

Cela me fournit une séparation logique, bien sûr, il n'y a rien de mal à les conserver models.py, mais c'est plus gérable de cette façon.

J'espère que cela t'aides!!

2
allsyed