2009-09-07 4 views
5

Voglio garantire che un oggetto sia univoco e generare un errore quando un utente tenta di salvarlo (ad esempio tramite l'amministratore) se no? Per unico, intendo che alcuni degli attributi dell'oggetto potrebbero contenere gli stessi valori di altri oggetti, ma non possono TUTTI essere identici ai valori di un altro oggetto.DRY oggetti unici in Django

Se non mi sbaglio, posso fare questo in questo modo:

class Animal(models.Model): 
    common_name = models.CharField(max_length=150) 
    latin_name = models.CharField(max_length=150) 
    class Meta: 
     unique_together = ("common_name", "latin_name") 

Ma poi ogni volta che refactoring del modello (ad esempio per aggiungere un nuovo campo, o per cambiare il nome di un campo esistente), Devo anche modificare l'elenco dei campi tra parentesi assegnato a unique_together. Con un modello semplice, va bene, ma con uno sostanziale, diventa una vera seccatura durante il refactoring.

Come evitare di dover ripetere la digitazione dell'elenco dei nomi dei campi nella parentesi unique_together? Esiste un modo per passare l'elenco dei campi del modello a una variabile e assegnare tale variabile a unique_together?

+1

+1 - grande domanda. Non penso che tu possa, ma sono interessato a sentire le opinioni degli altri. –

+0

+1 - È davvero interessante se qualcuno può trovare una soluzione piacevole. Ho provato ma non riuscivo a farlo funzionare tramite la riflessione, perché non puoi leggere le proprietà dalla classe Animal (che è ovvio perché Animal non è completamente definito in quel momento). – FlorianH

+0

jtiai su irc: //irc.freenode.net/django si è gentilmente preso un po 'di tempo per discutere la domanda e mi ha suggerito di aggiungere la mia classe "AllUniqueModel", ereditare lo standard "Model" e rendere magica la metaclass per iniettare tutti i campi in unique_togerther dopo che sono stati elaborati dalla procedura di creazione standard. ' Non sono del tutto sicuro di cosa intendesse jtiai, ma suona come una soluzione più complicata di quanto speravo. – sampablokuper

risposta

4

modelli di refactoring è una cosa piuttosto costoso da fare:

  • Sarà necessario cambiare tutto il codice utilizzando i modelli in quanto i nomi dei campi corrispondono alle proprietà degli oggetti
  • Sarà necessario modificare manualmente il database dal Django non si può fare questo per voi (almeno la versione che ho usato l'ultima volta in cui ho lavorato con Django non poteva)

Perciò penso che aggiorna l'elenco dei nomi dei campi univoci nella meta classe del modello è il minimo problema che si dovrebbe preoccuparsi a incontro.

EDIT: Se si vuole veramente fare questo e tutte dei campi deve essere "unico insieme", poi il ragazzo alla freenode è giusto e si dovrà scrivere un metaclasse personalizzato. Questo è abbastanza complicato e errorprone, in più potrebbe rendere il tuo codice incompatibile con le versioni future di Django.

La "magia" ORM di Django è controllata dalla metaclasse ModelBase (django.db.models.base.ModelBase) della classe base generica Model. Questa classe è responsabile di prendere la definizione della classe con tutti i campi e le informazioni Meta e costruire la classe che userete nel vostro codice in seguito.

Ecco una ricetta su come si potrebbe raggiungere il tuo obiettivo:

  1. sottoclasse ModelBase di utilizzare il proprio metaclasse.
  2. l'override del metodo __new__(cls, name, bases, dict)
  3. Ispezionare dict per raccogliere le Meta membro (dict["Meta"]), così come tutti i membri del campo
  4. Situato meta.unique_together in base ai nomi dei campi che si sono riuniti.
  5. chiamata il super implementazione (ModelBase.__new__)
  6. Utilizzare la metaclasse personalizzato per tutti i modelli unici utilizzando l'elemento magico __metaclass__ = MyMetaclass (o derivare una classe base astratta che si estende Model e ignorando l'metaclasse)
+1

Nella fase di prototipazione, i modelli di refactoring sono economici, ma la duplicazione del codice è costosa, da qui la mia domanda. Ovviamente, in produzione, è una questione diversa, ma se è possibile trovare una buona risposta alla mia domanda, questo ridurrà almeno * uno * dei passi che altrimenti dovrei fare quando refactoring in produzione. – sampablokuper

+1

Vedere la mia risposta aggiornata per la risposta lunga, forse capirai che potrebbe essere troppo complicato da fare. –

+2

Grazie per aver aggiornato la tua risposta. Sì, vedo che è piuttosto una seccatura per raggiungere questo obiettivo. Hmm. Sarebbe molto bello se Django potesse semplicemente fornire l'opzione per specificare ** unique \ _together = (all) ** o somesuch, o se permetterebbe ai modelli di eseguire introspezioni su se stessi per generare liste di campi per passare a opzioni Meta, ecc. . O entrambi :) – sampablokuper