2014-10-24 15 views
9

Io uso le migrazioni di Django 1.7 e, in particolare, voglio popolare un database appena creato con i dati iniziali. Pertanto, io uso una migrazione dei dati per questo. Ecco come si presenta:Come posso inviare segnali dalle migrazioni di Django?

def populate_with_initial_data(apps, schema_editor): 
    User = apps.get_model("auth", "User") 
    new_user = User.objects.create(username="nobody") 

class Migration(migrations.Migration): 

    ... 

    operations = [ 
     migrations.RunPython(populate_with_initial_data), 
    ] 

Allo stesso tempo, voglio avere un'istanza del modello UserDetails per ogni nuovo utente:

@receiver(signals.post_save, sender=django.contrib.auth.models.User) 
def add_user_details(sender, instance, created, **kwargs): 
    if created: 
     my_app.UserDetails.objects.create(user=instance) 

Ma: Questo segnale funziona solo al di fuori della migrazione . Il motivo è che apps.get_model("auth", "User") è abbastanza diverso da django.contrib.auth.models.User che nessun segnale viene inviato. Se provo a farlo manualmente, in questo modo, non riesce:

signals.post_save.send(django.contrib.auth.models.User, instance=julia, created=True) 

questo viene a mancare, perché poi, il gestore di segnale tenta di creare un nuovaUserDetails indicando con O2O ad un storicaUser:

ValueError: Cannot assign "<User: User object>": "UserDetails.user" must be a "User" instance. 

Bummer.

OK, potrei chiamare direttamente il gestore di segnale. Ma ho dovuto passare la classe storica UserDetails in un argomento di parole chiave (e altre classi storiche di cui ha bisogno). Inoltre, l'app con il UserDetails non è quella con questa migrazione dei dati, quindi questa sarebbe una brutta dipendenza che potrebbe facilmente rompersi, ad es. se l'app UserDetails viene rimossa da INSTALLED_APPS.

Quindi, si tratta semplicemente di una limitazione di corrente che devo risolvere con un brutto codice e un commento FixMe? O c'è un modo per inviare segnali dalle migrazioni dei dati?

+0

hai trovato una soluzione alternativa per questo? –

+2

Sì, utilizzando signal.post_migrate perché questo * è * chiamato. Ma ha ancora bisogno del codice che non dovrebbe essere necessario. –

+0

Dovresti pubblicare la risposta e accettare la tua risposta in quanto questa domanda è in cima alle domande senza risposta di django. – dotcomly

risposta

3

Non è possibile (e non dovrebbe) eseguire questa operazione perché quando viene eseguita la migrazione, il tuo UserDetails potrebbe essere molto diverso da quando hai scritto questa migrazione. Ecco perché django (e sud) usano "modelli congelati" che sono identici a quando hai scritto la migrazione.

"Sfortunatamente", è necessario bloccare il codice segnale nella migrazione per mantenere il comportamento previsto al momento della scrittura della migrazione.

Un semplice exemple per capire perché è importante non utilizzare modelli reali (o segnali, ecc) all'interno di una migrazione:

Oggi, ho potuto avere questo:

class UserDetails(models.Model): 
    user = models.ForeignKey(...) 
    typo_fild = models.CharField(...) 

@receiver(signals.post_save, sender=django.contrib.auth.models.User) 
def add_user_details(sender, instance, created, **kwargs): 
    if created: 
     UserDetails.objects.create(user=instance, typo_fild='yo') 

Poi, ho un migrazione dei dati (chiamata "populate_users") che crea nuovi utenti e impone l'esecuzione di add_user_details al suo interno. Va bene: funziona oggi.

Domani, ho risolto il mio typo_fild ->typo_field all'interno di UserDetails e all'interno di add_user_details. Viene creata una nuova migrazione dello schema per rinominare il campo nel database.

A questo punto, i miei migrazione "populate_users" falliranno perché quando verrà creato un nuovo utente, si cercherà di creare un nuovo UserDetails con un campo wich "typo_field" non esiste ancora nel database: questo il campo sarà rinominato nel DB con le migrazioni successive.

Quindi, se voglio mantenere una buona migrazione che funzionerà in qualsiasi momento, devo copiare il comportamento di add_user_details all'interno della migrazione. Questo blocco di add_user_details dovrà utilizzare il modello congelato UserDetails tramite apps.get_model("myapp", "UserDetails") e creare un nuovo UserDetails con il typo_fild che è anch'esso bloccato.

+1

Puoi sempre sparare ai piedi se lo desideri. Dopotutto, altri segnali * sono * inviati, e li uso per risolvere questo problema ora. Se si applicano modifiche problematiche al modello, è possibile utilizzare l'introspezione nel gestore di segnale. "Rendi semplice il caso frequente, e il caso raro è possibile." –

Problemi correlati