Dove dovrebbero vivere i gestori di segnali in un progetto Django?


143

Ho appena iniziato a implementare ascoltatori di segnali in un progetto Django. Mentre capisco cosa sono e come usarli. Sto facendo fatica a capire dove dovrei metterli. La documentazione dal sito django ha questo da dire:

Dove dovrebbe vivere questo codice?

Puoi inserire il codice di gestione e registrazione del segnale ovunque tu voglia. Tuttavia, dovrai assicurarti che il modulo in cui si trova venga importato in anticipo in modo che la gestione del segnale venga registrata prima di inviare qualsiasi segnale. Questo rende i modelli della tua app.py un buon posto per mettere la registrazione dei gestori di segnale.

Sebbene sia un buon suggerimento, avere classi o metodi non modello nei miei modelli.py mi sfrega semplicemente nel modo sbagliato.

Quindi, qual è la migliore pratica / regola per l'archiviazione e la registrazione di gestori di segnali?

Risposte:


41

In realtà mi piace renderli metodi di classe del modello stesso. Ciò mantiene tutto all'interno di una classe e significa che non devi preoccuparti di importare nulla.


2
E dove di solito colleghi i gestori ai segnali?
Data concordata il

1
@DataGreed: nella parte inferiore dei relativi modelli.py.
Daniel Roseman,

102
Se stai ascoltando i segnali emessi da quel modello, mettere anche tutti gli ascoltatori lì rende inutile l'intero esercizio, no? Il punto dei segnali è disaccoppiare. Gli ascoltatori non dovrebbero vivere con il codice che è interessato a questi eventi remoti? La domanda è come garantire che gli ascoltatori vengano caricati prima degli emettitori.
John Mee,

Nel mio caso, voglio ascoltare un segnale del modello di Foocui fa parte fooapp. Ma il ricevitore del segnale è un'estensione e vive in un'app diversa (ad esempio otherapp).
Guettli,

2
Secondo John Mee, non è molto diverso dalla semplice sostituzione di save (), ecc.
Matt,

246

Questo è stato aggiunto alla documentazione quando è stato rilasciato Django 1.7 :

A rigor di termini, la gestione del segnale e il codice di registrazione possono vivere ovunque tu voglia, anche se si consiglia di evitare il modulo radice dell'applicazione e il suo modulo modelli per ridurre al minimo gli effetti collaterali dell'importazione del codice.

In pratica, i gestori di segnali sono generalmente definiti in un sottomodulo di segnali dell'applicazione a cui si riferiscono. I ricevitori di segnali sono collegati nel metodo ready () della classe di configurazione dell'applicazione. Se stai usando il decoratore ricevitore (), importa semplicemente il sottomodulo dei segnali all'interno di ready ().

Modificato in Django 1.7: poiché ready () non esisteva nelle versioni precedenti di Django, la registrazione del segnale di solito avveniva nel modulo modelli.

La migliore pratica è definire i gestori in handlers.py in un sottomodulo di segnali, ad esempio un file che assomigli a:

yourapp / signal / 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

Il posto migliore per registrare il gestore del segnale è quindi nell'AppConfig dell'app che lo definisce, usando il metodo ready () . Questo sarà simile al seguente:

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

Assicurati di caricare AppConfig specificandolo direttamente nelle tue settings.pY INSTALLED_APPS o nel __init__ tua app. Vedere la documentazione ready () per ulteriori informazioni.

Nota: se stai fornendo segnali anche per altre app da ascoltare, inseriscili nel __init__modulo dei segnali, ad esempio un file che assomigli a:

YourApp / segnali / __ init__.py

import django.dispatch

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

Un'altra app può quindi ascoltare il tuo segnale importandolo e registrandolo, ad es from yourapp.signals import task_generate_pre_save . Separare i segnali dai gestori mantiene le cose pulite.

Istruzioni per Django 1.6:

Se sei ancora bloccato su Django 1.6 o precedente, allora faresti la stessa cosa (definisci i tuoi gestori in yourapp / signal / handlers.py) ma invece di usare AppConfig, caricheresti i gestori tramite __init__.py di la tua app, ad esempio qualcosa del tipo:

YourApp / __ init__.py

import signals

Questo non è bello come usare il metodo ready () perché spesso causa problemi di importazione circolare.


3
poiché il documentaiton dice che hai la precedenza, potresti voler fare qualcosa di simile a super (ReportsConfig, self) .ready () nel caso in cui django decida di popolare ready () con qualcosa (a partire dalla 1.7.0 è attualmente vuoto)
w- -

3
Penso che questa risposta sia la migliore perché è l'unica ad affrontare gli effetti collaterali delle importazioni. Sono venuto qui alla ricerca delle migliori pratiche, perché sto pulendo un'applicazione, che si interrompe esattamente a causa di questo tipo di effetti collaterali. Purtroppo l'applicazione funziona su django 1.6 e le migliori pratiche funzionano solo su django 1.7. La soluzione temporanea di lasciare che __init__i segnali di importazione non funzionino per me, quindi mi chiedo se c'è un altro posto da cui posso importare i segnali fino a quando non siamo pronti per l'aggiornamento a una versione successiva di Django.
Kasperd,

Non dovrebbe esserci from . import handlers(o simile) in yourapp/signals/__init__.py?
Dhobbs,

Non dovresti anche importare il modulo handlers.py da qualche parte? Ci sto provando e non sembra definire l'handler per il segnale.
Andrés,

1
prima non avevo bisogno yourproject.dell'ultima riga del blocco di codice della classe TaskConfig. Ho funzionato esattamente con questa struttura, quindi considera questo qa :)
Greg Kaleka,

40

Mi sono appena imbattuto in questo, e poiché i miei segnali non sono legati al modello, ho pensato di aggiungere la mia soluzione.

Sto registrando vari dati relativi all'accesso / disconnessione e dovevo collegarmi django.contrib.auth.signals .

Ho inserito i gestori dei segnali in un signals.pyfile e quindi __init__.pyho importato i segnali dal file del modulo, poiché credo che questo venga chiamato non appena si avvia l'app (il test con printun'istruzione suggerisce che viene chiamato anche prima della lettura del file delle impostazioni).

# /project/__init__.py
import signals

e in signal.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)

Sono abbastanza nuovo con Django (/ python), quindi sono aperto a chiunque mi dica che questa è un'idea terribile!


3
Sembra logico, ma suggerirei di farlo a livello di app.
Nils,

2
Attenzione, questa logica molto probabilmente si tradurrà in segnali doppi sparati. user_logged_in.connect(on_logged_in)molto probabilmente dovrebbe passare l' dispatch_uidargomento. Maggiori informazioni su docs.djangoproject.com/en/dev/topics/signals/… .
Scott Coates,

Grazie per questo - buono a sapersi. Sto registrando tutti gli accessi usando questo metodo (registrazione IP / user agent), e finora non ho avuto duplicati - anche se questo non significa che una piccola modifica lungo la linea non causerà un problema!
Hugo Rodger-Brown,

13

Di recente ho letto questo articolo sulle migliori pratiche quando si tratta di presentare i tuoi progetti / applicazioni, e suggerisce che tutti i segnali del tuo dispatcher personalizzato dovrebbero andare in un file chiamatosignals.py . Tuttavia, ciò non risolve completamente il problema, poiché è ancora necessario importarli da qualche parte e prima vengono importati meglio è.

Il suggerimento del modello è buono. Dato che hai già definito tutto nel tuo signals.pyfile, non dovrebbe essere necessario più di una riga nella parte superiore del file. Questo è simile al modo in cuiadmin.py file è disposto (con le definizioni delle classi nella parte superiore e il codice per la registrazione di tutte le classi di amministrazione personalizzate nella parte inferiore), se si definiscono i segnali, collegarli nello stesso file.

Spero che aiuti! Alla fine si riduce a ciò che preferisci.


1
Volevo anche mettere i miei gestori di segnale in un signals.pyfile, ma non sapevo come avrebbe dovuto essere chiamato in seguito. Importandolo nel mio models.pyfile, ho ottenuto una soluzione molto pulita, senza "inquinare" il mio file models.py. Grazie! :)
Danilo Bargen,

10
c'è un'importazione incrociata lì: signal.py tenta di importare il modello da models.py
Ivan Virabyan il

8

models.py e signal.py in ogni app sono stati i luoghi consigliati per connettere i segnali, tuttavia, a mio avviso, non sono la soluzione migliore per mantenere i segnali e i gestori inviati. Il dispacciamento dovrebbe essere il motivo per cui segnali e gestori inventati nel django.

Stavo lottando da molto tempo e finalmente abbiamo trovato la soluzione.

crea un modulo connettore nella cartella dell'app

quindi abbiamo:

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

in app / connettori.py, abbiamo definito i gestori di segnale e li abbiamo collegati. Un esempio è fornito:

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)

quindi in models.py, aggiungiamo la seguente riga alla fine del file:

from app import connector

Tutto fatto qui.

In questo modo, possiamo inserire i segnali in signal.py e tutti i gestori in connettori.py. Nessun pasticcio nei modelli e nei segnali.

Spero che fornisca un'altra soluzione.


1
Quindi cosa succede in signal.py? Sembra che dal tuo esempio siano solo i segnali personalizzati. Di solito combiniamo solo segnali e connettori poiché la maggior parte non avrà segnali personalizzati.
Dalore,

@dalore sì, tutti i segnali personalizzati vengono inseriti in signal.py. Abbiamo molti segnali personalizzati. Ma se non ne hai molti, questo file potrebbe essere omesso.
samuel,

stessa domanda di @dal
olleh

1
notate che tutto ciò è ora un vecchio consiglio, il modo django ora è usare appconfig per importare gestori in cui vivono i gestori di segnale. E in signal.py vai segnali personalizzati
dalore

3

Le conservo in un file separato signals.py, models.pydopo aver definito tutti i modelli. Li importa e connetto i modelli ai segnali.

signals.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)

Questo mi fornisce una separazione logica, ovviamente non c'è nulla di sbagliato nel tenerli nei modelli . Spia , ma è più gestibile in questo modo.

Spero che questo ti aiuti!!


stai inserendo i gestori dei segnali in "signal.py", e se lo chiamassimo "handlers.py"
Abdul Fatah,

1
Non importa se si denomina il file come signal.py o handler.py. È solo una convenzione, non una regola.
allsyed

3

Piccolo promemoria in merito AppConfig. Non dimenticare di impostare:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.