web-dev-qa-db-fra.com

Le bon endroit pour conserver mon fichier signaux.py dans un projet Django

D'après la documentation de Django que je lisais, il semble que signals.py dans le dossier de l'application est un bon point de départ, mais le problème auquel je suis confronté est que lorsque je crée des signaux pour pre_save et j'essaie d'importer la classe à partir du modèle en conflit avec le import dans mon modèle.

# models.py

from Django.contrib.auth.models import User
from Django.db import models
from Django.utils.translation import gettext as _
from signals import *

class Comm_Queue(CommunicatorAbstract):
    queue_statuses = (
        ('P', _('Pending')),
        ('S', _('Sent')),
        ('E', _('Error')),
        ('R', _('Rejected')),
    )
    status          = models.CharField(max_length=10, db_index=True, default='P')
    is_html         = models.BooleanField(default=False)
    language        = models.CharField(max_length=6, choices=settings.LANGUAGES)
    sender_email    = models.EmailField()
    recipient_email = models.EmailField()
    subject         = models.CharField(max_length=100)
    content         = models.TextField()

# signals.py

from Django.conf import settings
from Django.db.models.signals import pre_save
from Django.dispatch import receiver
from models import Comm_Queue

@receiver(pre_save, sender=Comm_Queue)
def get_sender_email_from_settings(sender, **kwargs):
    obj=kwargs['instance']
    if not obj.sender_email:
        obj.sender_email='%s' % settings.ADMINS[0][1]

Ce code ne fonctionnera pas car j'importe Comm_Queue à l'intérieur signals.py et j'importe également les signaux à l'intérieur models.py.

Quelqu'un peut-il me conseiller sur la façon de surmonter ce problème?

Cordialement

75
Mo J. Mughrabi

Réponse originale, pour Django <1.7:

Vous pouvez enregistrer les signaux en important signals.py Dans le fichier __init__.py De l'application:

# __init__.py
import signals

Cela permettra d'importer models.py À partir de signals.py Sans erreurs d'importation circulaires.

Un problème avec cette approche est qu'elle gâche les résultats de la couverture si vous utilisez la couverture.py.

Discussion connexe

Modifier: Pour Django> = 1.7:

Depuis l'introduction d'AppConfig, la manière recommandée d'importer des signaux est dans sa fonction init(). Voir réponse d'Eric Marcos pour plus de détails.

61
yprez

Si vous utilisez Django <= 1.6, je recommanderais la solution Kamagatos: importez simplement vos signaux à la fin de votre module de modèles.

Pour les futures versions de Django (> = 1.7), la manière recommandée consiste à importer votre module de signaux dans la configuration de votre application ready () une fonction:

my_app/apps.py

from Django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

my_app/__init__.py

default_app_config = 'my_app.apps.MyAppConfig'
174
Eric Marcos

Pour résoudre votre problème, il vous suffit d'importer signaux.py après la définition de votre modèle. C'est tout.

24
Kamagatos

J'ai également mis des signaux dans le fichier signaux.py et j'ai également cet extrait de code qui charge tous les signaux:

# import this in url.py file !

import logging

from importlib import import_module

from Django.conf import settings

logger = logging.getLogger(__name__)

signal_modules = {}

for app in settings.INSTALLED_APPS:
    signals_module = '%s.signals' % app
    try:
        logger.debug('loading "%s" ..' % signals_module)
        signal_modules[app] = import_module(signals_module)
    except ImportError as e:
        logger.warning(
            'failed to import "%s", reason: %s' % (signals_module, str(e)))

C'est pour le projet, je ne sais pas si cela fonctionne au niveau de l'application.

5
aisbaa

Dans les anciennes versions Django serait bien de placer les signaux sur le __init__.py ou peut-être dans le models.py (bien qu'à la fin les modèles seront bien trop grands à mon goût).

Avec Django 1.9, il vaut mieux je pense, placer les signaux sur un signals.py fichier et importez-les avec le apps.py, où ils seront chargés après le chargement du modèle.

apps.py:

from Django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

    def ready(self):
        from . import signals  # NOQA

Vous pouvez également diviser vos signaux sur signals.py et handlers.py dans un autre dossier de votre modèle nommé signals également, mais pour moi, c'est un peu plus que l'ingénierie. Jetez un oeil à Placement des signaux

4
Tyson Rodez

Je suppose que vous le faites pour que vos signaux soient enregistrés, afin qu'ils soient trouvés quelque part. Je mets juste mes signaux directement dans un fichier models.py normalement.

3
Issac Kelly

Une alternative consiste à importer les fonctions de rappel depuis signals.py et connectez-les dans models.py:

signaux.py

def pre_save_callback_function(sender, instance, **kwargs):
    # Do stuff here

model.py

# Your imports here
from Django.db.models.signals import pre_save
from yourapp.signals import pre_save_callback_function

class YourModel:
    # Model stuff here
pre_save.connect(pre_save_callback_function, sender=YourModel)

Ps: importation de YourModel dans signals.py créera une récursivité; utilisez plutôt sender.

Ps2: Enregistrer à nouveau l'instance dans la fonction de rappel créera une récursivité. Vous pouvez créer un argument de contrôle dans .save méthode pour le contrôler.

1
Rafael

Cela ne s'applique que si vous avez vos signaux dans un fichier signals.py Séparé

En tout à fait d'accord avec la réponse de @EricMarcos mais il faut préciser que le Django docs conseille explicitement de ne pas utiliser la variable default_app_config (bien que ce ne soit pas faux). Pour les versions actuelles, la manière correcte serait:

my_app/apps.py

from Django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

settings.py

(Assurez-vous que vous n'avez pas seulement le nom de votre application dans les applications installées, mais plutôt le chemin relatif vers votre AppConfig)

INSTALLED_APPS = [
    'my_app.apps.MyAppConfig',
    # ...
]
0
Xen_mar