2015-10-21 6 views
11

Ho incontrato questo problema un paio di volte in diverse situazioni, ma la mia configurazione è la seguente:Python/Django: Perché importare un modulo prima di usarlo impedisce un'importazione circolare?

Ho due file di modelli Django. Uno che contiene modelli utente e codici coupon che un utente può utilizzare per iscriversi a un corso. Questi sono entrambi nel file account/models.py. Il corso e il relativo campo molti a molti sono in un diverso file di modelli, course/models.py. Solitamente mi riferisco a questi nel mio codice rispettivamente come amod e cmod.

Nel corso/models.py ho un'istruzione import:

from account import models as amod 

class Course(ExtendedModel): 
    stuff = stuff 

ho bisogno di importare il file dell'account/models.py per i molti-a-molti modelli/tavolo tra Course e l'utente che è non mostrato qui. Fin qui tutto bene.

Nel file account/models.py ho il modello CouponCode. Ogni istanza viene creata e quindi può essere assegnata a un particolare oggetto Course dopo la creazione per consentire a uno studente di utilizzarlo per iscriversi a un corso nel sistema.

class CouponCode(ExtendedModel): 
    assigned_course = UniqueIDForeignKey("course.Course", blank=True, null=True, related_name='assigned_coupon_code_set') 
    ... 
    ... 

    @staticmethod 
    def assign_batch(c, upper_limit): 
     import course.models as cmod # Why is this allowed here? 
     assert isinstance(c, cmod.Course) 

     # Do other stuff here 

Questo metodo statico mi permette di passare in un oggetto da golf e una serie di CouponCodes che voglio assegnare ad esso e poi si assegnerà il prossimo numero N di codici non assegnati a quel corso. La mia domanda nasce dalla dichiarazione di affermazione.

Ho bisogno di importare l'oggetto Course da course/models.py per assicurare che l'oggetto che viene passato sia in realtà un'istanza di Course, ma se lo faccio all'inizio del file, ho problemi perché questo file è già stato importato in course/models.py. (l'importazione viene importata in cmod e quindi in amod devo importare cmod).

Perché mi consente di farlo se lo impongo nel metodo giusto prima che ne abbia bisogno rispetto all'inizio del file?

risposta

8

Quando un modulo viene importato (beh, la prima volta che viene importato in un determinato processo), tutte le istruzioni di primo livello vengono eseguite (ricordiamo che importè un'istruzione eseguibile). Pertanto, non è possibile avere module1 con un'istruzione import module2 al livello principale e module2 con un import module1 al livello superiore, non può ovviamente funzionare.

Ora se nel modulo2 si sposta l'istruzione import module1 all'interno di una funzione, questa istruzione non verrà eseguita prima che la funzione venga effettivamente chiamata, quindi non impedirà a module1 di importare il modulo2.

Si noti che questo è ancora considerato una cattiva pratica, il più delle volte una dipendenza circolare significa che si dovrebbe refactoring il codice per evitare il problema (estrarre le parti entrambi i moduli dipende da un terzo modulo che dipende da entrambi ma nessuno dei altri dipendono o semplicemente uniscono i moduli), ma alcuni casi sono complessi a causa di altri vincoli, quindi avere una soluzione di ultima istanza va bene.

Inoltre, non bisogno di importare un modello di riferimento in un campo ForeignKey o Many2Many - è possibile passare una "appname.ModelName" stringa, cf https://docs.djangoproject.com/en/1.8/ref/models/fields/#foreignkey

Per fare riferimento a modelli definiti in un'altra applicazione, è possibile esplicitamente specificare un modello con l'etichetta completa dell'applicazione.Ad esempio, se il modello Costruttore sopra è definito in un'altra applicazione chiamata produzione, avresti bisogno di usare:

class Car(models.Model): 
    manufacturer = models.ForeignKey('production.Manufacturer') 

Questo tipo di riferimento può essere utile per la risoluzione di importazione circolare dipendenze tra le due applicazioni.

+0

Nella tua esperienza, è mai esistito un motivo per non utilizzare il riferimento di stringa durante la creazione di una chiave esterna rispetto a un riferimento alla classe stessa? Per me sembra che farlo sempre sarebbe un buon modo per evitare problemi. – Spartacus

+0

@Spartacus risposta tardiva mi dispiace - beh, l'uso di un riferimento di stringa obbliga l'orm a fare alcune ricerche aggiuntive e alla fine importa per risolvere il riferimento in modo da avere una leggera penalizzazione all'avvio del processo. Se questo è un problema o meno è lasciato al tuo apprezzamento ... Ma potresti rivolgere la domanda al contrario: perché usare un riferimento di stringa quando hai già a disposizione la classe? -) –

Problemi correlati