2009-11-25 11 views
13

Spero che tutto ciò abbia senso :) Se necessario, chiarirò tramite i commenti. Inoltre, sto sperimentando usando il testo in grassetto in questa domanda, e lo modificherò se io (o voi) lo troviate fonte di distrazione. Con questo fuori strada ...Django Multi-Table Inheritance VS Specifica esplicita relazione OneToOne nei modelli

Utilizzando django.contrib.auth ci fornisce Utente e Gruppo, tra le altre cose utili di cui non posso fare a meno (come la messaggistica di base).

Nella mia app ho diversi tipi di utenti. Un utente può essere di un solo tipo. Questo sarebbe facilmente gestito dai gruppi, con un po 'di attenzione in più. Tuttavia, questi diversi utenti sono correlati l'uno all'altro nelle gerarchie/relazioni.

Diamo uno sguardo a questi utenti: -

presidi - utenti "alto livello"

amministratori - ogni amministratore riferisce a un'entità

coordinatori - ciascun coordinatore riferisce a un amministratore

Oltre a questi ci sono altri tipi di utenti che non sono correlati direttamente, ma potrebbero essere correlati in seguito. Ad esempio, "Azienda" è un altro tipo di utente e può avere vari "Prodotti" e i prodotti possono essere supervisionati da un "Coordinatore". "Acquirente" è un altro tipo di utente che può acquistare prodotti.

Ora tutti questi utenti di hanno vari altri attributi, alcuni dei quali sono comuni a tutti i tipi di utenti e alcuni sono distinti solo da un tipo di utente. Ad esempio, tutti i tipi di utenti devono avere un indirizzo. D'altra parte, solo l'utente principale appartiene a un "BranchOffice".

Un altro punto, che è stato affermato sopra, è che un utente può essere sempre di un solo tipo.

L'app anche deve tenere traccia di chi ha creato e/o modificato Principali, Amministratori, Coordinatori, Aziende, Prodotti ecc.. (Ecco, questo è più di due collegamenti al modello User.)

In questo scenario, è una buona idea usare multi-tavolo l'eredità di Django come segue: -

from django.contrib.auth.models import User 
class Principal(User): 
    # 
    # 
    #  
    branchoffice = models.ForeignKey(BranchOffice) 
    landline = models.CharField(blank=True, max_length=20)  
    mobile = models.CharField(blank=True, max_length=20) 
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")  
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier") 
    # 
    # 
    # 

O devo andare a farlo in questo modo: -

class Principal(models.Model): 
    # 
    # 
    # 
    user = models.OneToOneField(User, blank=True) 
    branchoffice = models.ForeignKey(BranchOffice) 
    landline = models.CharField(blank=True, max_length=20)  
    mobile = models.CharField(blank=True, max_length=20) 
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")  
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier") 
    # 
    # 
    # 

si prega di tenere a mente che ci sono altri tipi di utenti che sono collegati tramite chiavi esterne, ad esempio: -

class Administrator(models.Model): 
    # 
    # 
    # 
    principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator") 
    user = models.OneToOneField(User, blank=True) 
    province = models.ForeignKey(  Province) 
    landline = models.CharField(blank=True, max_length=20)  
    mobile = models.CharField(blank=True, max_length=20) 
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")  
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier") 

Sono consapevole del fatto che Django utilizza una relazione uno a uno per l'ereditarietà multi-tavolo dietro le quinte. Non sono abbastanza qualificato per decidere quale sia un approccio più sano.

risposta

14

Mi piacerebbe espandere la soluzione di @thornomad.

Estendere direttamente la classe utente di Django può causare tutti i tipi di problemi con i meccanismi interni di django.auth. Quello che ho fatto in una situazione simile è esattamente ciò che suggerisce @thornomad: ho creato il mio modello UserProfile collegato a uno a uno con il modello utente Django, in cui contenevo ulteriori dati utente e da cui ho ereditato modelli per diversi tipi degli utenti.

Qualcosa per adattarsi a quello che hai descritto:

class UserProfile(models.Model): 
    user = models.OneToOneField(User, blank=True, related_name='profile') 
    class Meta: 
     abstract = True 


class PositionHolderUserProfile(UserProfile): 
    first_name = models.CharField(max_length=30) 
    last_name = models.CharField(max_length=30) 
    landline = models.CharField(blank=True, max_length=20)  
    mobile = models.CharField(blank=True, max_length=20) 
    created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")  
    modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users") 

class Principal(PositionHolderUserProfile): 
    branchoffice = models.ForeignKey(BranchOffice) 

class Administrator(PositionHolderUserProfile): 
    superior = models.ForeignKey(Principal, related_name="subordinates") 
    province = models.ForeignKey(Province) 

class Coordinator(PositionHolderUserProfile): 
    superior = models.ForeignKey(Administrator, related_name="subordinates") 


class Company(UserProfile): 
    name = models.CharField(max_length=50) 

class Product(models.Model): 
    name = models.CharField(max_length=50) 
    produced_by = models.ForeignKey(Company) 

class Buyer(UserProfile): 
    first_name = models.CharField(max_length=30) 
    last_name = models.CharField(max_length=30) 
    products_bought = models.ManyToManyField(Product) 
+0

Mi sembra una buona implementazione. Ma perché hanno sia la classe UserProfile sia la classe PositionHolderUserProfile? Non sarà più semplice sbarazzarsi di quest'ultimo e spingere tutto in esso al modello precedente? – chefsmart

+0

Ho pensato che fosse meglio incapsulare i dati relativi a tutti i detentori di posizione, ad es. Principale, Amministratore e Coordinatore, ma non rilevante per altri utenti come Acquirente e Azienda. Tali dati potrebbero includere in quale ramo lavorano, quanto tempo sono stati impiegati, ecc. – taleinat

+0

Questo non sembra funzionare con le impostazioni.AUTH_PROFILE_MODULE. –

2

Non credo che erediterei il modello User, piuttosto utilizzare un modello personalizzato UserProfile - lasciando il modello contrib.auth da solo.Con il modello personalizzato UserProfile, è possibile impostare un modello di profilo utente di base che può far parte di tutti i diversi tipi di utente.

Solo guardandolo rapidamente, guarderei attentamente tutti i modelli che ripetono tutti gli stessi campi (come i tuoi ultimi due modelli Principle e Administrator). Combinare la funzionalità integrata del gruppo con l'idea del profilo utente può fare ciò che stai cercando.

+0

Ci sono altri tipi di utenti che vorrei davvero mantenere separati. Quando guardo i miei modelli, mi piacerebbe vedere che il Preside, l'Amministratore, il Coordinatore sono collegati, ma anche che la Società e il Compratore non sono direttamente collegati a nessuno di questi. – chefsmart

+0

Giusto per chiarire, intendo utilizzare modelli astratti in cui sia utile. Inoltre, ho intenzione di avere modelli separati per diversi tipi di utenti. – chefsmart

3

Recentemente ho passato a utilizzare i modelli che ereditano da contrib.auto.models.User. La mia osservazione generale è che, in teoria, sono fantastici, ma a volte non riescono a gestirsi auto magicamente come dovrebbero.

Credo che la vostra decisione per quanto riguarda l'eredità vs OnetoOne si riduce a questo:

  • voglio avere Django fare automaticamente qualcosa di destra il 95% del tempo, e la necessità di eseguire il debug che altro 5%

OPPURE

  • voglio fare qualcosa manualmente me stesso al 100% del tempo

Se non lo si è visto, il blog di Scott Barham ha un ottimo post sull'ereditarietà dell'Utente e sulla creazione di un back-end personalizzato per assicurarsi che venga restituito l'oggetto personalizzato - Extending the Django User.

Oltre all'interesse sarebbe il campo AutoOneToOne fornito da django-annoying. È una specie di ibrido tra i due approcci qui: non c'è nessuna eredità in atto, ma Django si sta occupando della creazione del OneToOneField corrispondente, se non è presente.

Inoltre, thornomad fa un buon punto sulla ridondanza nei modelli. Si potrebbe facilmente implementare una classe astratta per la pulizia che come modo (supponendo che si sta facendo OnetoOne manuale):

class BaseExtendedUser(models.Model): 
    user = models.OneToOneField(User, blank=True, related_name='profile') 
    landline = models.CharField(blank=True, max_length=20)  
    mobile = models.CharField(blank=True, max_length=20) 
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")  
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users") 

    class Meta: 
     abstract = True 

class Administrator(BaseExtendedUser): 
    province = models.ForeignKey(Province) 

class Principal(BaseExtendedUser): 
    branchoffice = models.ForeignKey(BranchOffice) 
+0

> ma a volte non vengono gestiti automaticamente come si suppone. Potresti espanderti e farci sapere come non si sono comportati come previsto? – chefsmart

+0

È difficile qualificarsi, ma la maggior parte dei problemi riguarda contrib.admin, credo. Occasionalmente si verificano problemi dispari, ad esempio se si esegue l'override di AdminModel.model_save() che ho trovato (tramite un'ora + di sperimentazione) è necessario specificare manualmente force_insert o force_update altrimenti non creerà il corrispondente OneToOne. Occasionalmente la vera interfaccia di amministrazione si oscura e dice che esiste già un record che esiste con quell'ID quando hai appena aggiornato qualche valore. –

+0

Devo indagare in tal caso, ma comunque non sto usando contrib.admin. Sto ancora cercando di scoprire i pro e i contro di Multi-Table Inheritance VS Explicit OneToOne Relationship – chefsmart

0

perche ciò che accade nel modello di dati quando un coordinatore viene promosso a un Principal. Non userei affatto l'ereditarietà in questo caso. Ti preghiamo di riconsiderare il suggerimento del precedente poster "Combinare la funzionalità incorporata del gruppo con l'idea del profilo utente potrebbe fare ciò che stai cercando."

+0

Non c'è nessuna promozione in corso. "Combinare la funzionalità integrata del gruppo con l'idea del profilo utente può fare ciò che stai cercando." è una buona pratica e la seguo, tuttavia voglio sapere quali sono i pro ei contro dell'ereditarietà rispetto a quelli espliciti in questo particolare scenario ... – chefsmart

0

Avete bisogno che gli oggetti delle classi utente si comportino come un'autenticazione dell'utente ovunque? Questa sarebbe la ragione più ovvia per usare l'ereditarietà su OneToOne. Un professionista dell'approccio OneToOne sarebbe la facilità con cui è possibile passare a un altro modello utente, se questo è un problema.

Il vero problema che vedo con quello che hai sopra (con entrambi i metodi) è che non sembra esserci nulla che ti impedisca di avere un oggetto Principal e un oggetto Amministratore condividere lo stesso Utente. OneToOneField può garantire una mappatura uno-a-uno tra due relazioni qualsiasi.

+0

Ora vado con OneToOne perché so esattamente cosa sta succedendo lì, invece di dover indovinare tutte le ripercussioni dell'ereditarietà (come ad esempio il UserManager). Avere maggiore controllo sembra una proposta attraente. – chefsmart

Problemi correlati