2011-10-25 9 views
15

Sto costruendo un progetto personale con Django, per allenarmi (perché amo Django, ma mi mancano le abilità). Ho i requisiti di base, conosco Python, ho letto attentamente il libro di Django due volte se non tre volte.ForeignKey alla classe astratta (relazioni generiche)

Il mio obiettivo è creare un semplice servizio di monitoraggio, con un'interfaccia web basata su Django che mi consenta di controllare lo stato dei miei "nodi" (server). Ogni nodo ha più "servizi". L'applicazione controlla la disponibilità di ciascun servizio per ciascun nodo.

Il mio problema è che non ho idea di come rappresentare diversi tipi di servizi nel mio database. Ho pensato a due "soluzioni":

  • singolo modello di servizio, con un campo "serviceType", e un gran casino con i campi. (Non ho una grande esperienza nella modellazione di database, ma questo sembra ... "cattivo" per me)
  • più modelli di servizio. Mi piace questa soluzione, ma poi non ho idea di come posso fare riferimento a questi servizi DIFFERENT nello stesso campo.

Questo è un breve estratto dal mio models.py file: (ho tolto tutto ciò che non è legato a questo problema)

from django.db import models 

# Create your models here.                               
class service(models.Model): 
    port = models.PositiveIntegerField() 
    class Meta: 
     abstract = True 

class sshService(service): 
    username = models.CharField(max_length=64) 
    pkey = models.TextField() 

class telnetService(service): 
    username = models.CharField(max_length=64) 
    password = models.CharField(max_length=64) 

class genericTcpService(service): 
    pass 

class genericUdpService(service): 
    pass 

class node(models.Model): 
    name = models.CharField(max_length=64) 
    # various fields                                 
    services = models.ManyToManyField(service) 

Naturalmente, la linea con il ManyToManyField è fasullo. Non ho idea di cosa mettere al posto di "* Servizio". Ho cercato sinceramente soluzioni a questo proposito, ho sentito parlare di "relazioni generiche", tabelle triple-join, ma non ho davvero capito queste cose.

Inoltre, l'inglese non è la mia lingua madre, in modo da venire a struttura del database e la semantica, la mia conoscenza e la comprensione di ciò che ho letto è limitato (ma questo è il mio problema)

risposta

13

Per iniziare, utilizzare il modello multi-table inheritance di Django, anziché il modello astratto che si ha al momento.

Il tuo codice sarebbe poi diventato:

from django.db import models 

class Service(models.Model): 
    port = models.PositiveIntegerField() 

class SSHService(Service): 
    username = models.CharField(max_length=64) 
    pkey = models.TextField() 

class TelnetService(Service): 
    username = models.CharField(max_length=64) 
    password = models.CharField(max_length=64) 

class GenericTcpService(Service): 
    pass 

class GenericUDPService(Service): 
    pass 

class Node(models.Model): 
    name = models.CharField(max_length=64) 
    # various fields                                 
    services = models.ManyToManyField(Service) 

A livello di database, questo creerà una tabella di 'servizio', le file dei quali saranno collegati tramite uno a uno i rapporti con tavoli separati per ogni servizio bambino .

L'unica difficoltà di questo approccio è che quando si fa qualcosa di simile al seguente:

node = Node.objects.get(pk=node_id) 

for service in node.services.all(): 
    # Do something with the service 

Il 'servizio' oggetti l'accesso nel circuito sarà di tipo genitore. Se sai cosa bambino digitare questi avranno in anticipo, si può solo accedere alla classe bambino nel modo seguente:

from django.core.exceptions import ObjectDoesNotExist 

try: 
    telnet_service = service.telnetservice 
except (AttributeError, ObjectDoesNotExist): 
    # You chose the wrong child type! 
    telnet_service = None 

Se non si conosce il tipo di bambino in anticipo, diventa un po 'più complicato. Ci sono alcune soluzioni hacky/disordinate, incluso un campo "serviceType" sul modello principale, ma un modo migliore, come ha detto Joe J, è di usare un "queryset di sottoclassi". La classe InheritanceManager di django-model-utils è probabilmente la più facile da usare. Leggi la documentazione per questo here, è davvero un bel pezzetto di codice.

+0

Grazie per il dettaglio, pieno di codice, risposta. Con quello di @Joe J, sono abbastanza sicuro che mi aiuterà durante la modellazione della mia applicazione. Questo sito è fantastico, anche i suoi utenti :) – pistache

+0

OK, questa è stata un'ottima soluzione che hai fornito qui, in particolare il trucco InheritanceManager e l'intero pacchetto django-model-utils. Grazie ancora – pistache

6

penso un approccio che si potrebbe prendere in considerazione è un "queryset di sottoclassi". Fondamentalmente, ti permette di interrogare il modello genitore e restituirà le istanze dei modelli figli nel queryset dei risultati. Sarebbe farvi fare domande come:

models.service.objects.all() 

e farlo tornare a risultati come la seguente:

[ <sshServiceInstance>, <telnetServiceInstance>, <telnetServiceInstance>, ...] 

Per alcuni esempi su come fare questo, controllare i link sul post sul blog collegato sotto.

http://jazstudios.blogspot.com/2009/10/django-model-inheritance-with.html

Tuttavia, se si utilizza questo metodo, non si deve dichiarare il vostro modello di servizio come astratto come si fa l'esempio. Certo, introdurrai un join aggiuntivo, ma nel complesso ho trovato che il queryset di sottoclassi funziona piuttosto bene per restituire un gruppo misto di oggetti in un set di query.

In ogni caso, spero che questo aiuta, Joe

+1

Grazie mille, la tua risposta, con quella di @Voightkampff, mi ha aiutato a capire l'ereditarietà del modello e mi ha dato un nuovo modo di pensare alla mia struttura dati del modello. Da parte mia, questa è stata la mia prima domanda in stackoverflow.com, e sono davvero felice con il sito web, l'interfaccia, gli utenti, le risposte e sarei lieto di condividere anche le mie conoscenze, ora. :) – pistache

0

Se siete alla ricerca di generici relazioni di chiave esterna si dovrebbe verificare il Django contenttypes framework (integrato in Django). I documenti spiegano praticamente come usarlo e come lavorare con relazioni generiche.

+1

Grazie, ma come ho detto, l'ho già controllato, ma non ho davvero capito né ho potuto mappare gli esempi su Internet al mio caso d'uso. – pistache

0

Un servizio effettivo può essere solo su un nodo, giusto? In questo caso, quando non hanno un campo

node = models.ForeignKey('node', related_name='services') 

nella classe service?