2011-10-25 14 views
15

Estoy construyendo un proyecto personal con Django, para entrenarme a mí mismo (porque amo a Django, pero echo de menos las habilidades). Tengo los requisitos básicos, conozco a Python, leo detenidamente el libro de Django dos o tres veces.ForeignKey a la clase abstracta (relaciones genéricas)

Mi objetivo es crear un servicio de supervisión simple, con una interfaz web basada en Django que me permita verificar el estado de mis "nodos" (servidores). Cada nodo tiene múltiples "servicios". La aplicación verifica la disponibilidad de cada servicio para cada nodo.

Mi problema es que no tengo idea de cómo representar diferentes tipos de servicios en mi base de datos. Pensé en dos "soluciones":

  • sola modelo de servicio, con un campo "tipoServicio", y un gran lío con los campos. (No tengo una gran experiencia en el modelado de bases de datos, pero esto se ve ... "malo" para mí)
  • múltiples modelos de servicio. Me gusta esta solución, pero no tengo idea de cómo puedo hacer referencia a estos DIFERENTES servicios en el mismo campo.

Este es un breve extracto de mi models.py archivo: (He quitado todo lo que no esté relacionada con este 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) 

Por supuesto, la línea con el ManyToManyField es falso. No tengo idea de qué poner en lugar de "* Servicio". Honestamente busqué soluciones sobre esto, escuché sobre "relaciones genéricas", tablas de triple unión, pero realmente no entendía estas cosas.

Por otra parte, el Inglés no es mi lengua materna, por lo que viene a la estructura de base de datos y la semántica, mi conocimiento y comprensión de lo que he leído es limitado (pero eso es mi problema)

Respuesta

13

Para empezar, utilice multi-table inheritance de Django, en lugar del modelo abstracto que tiene actualmente.

Su código se convertiría entonces en:

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) 

En el nivel de base de datos, esto creará una tabla de 'servicio', las filas de los cuales estarán conectados a través de uno a uno las relaciones con tablas separadas para cada servicio hijo .

La única dificultad con este enfoque es que cuando se hace algo como lo siguiente:

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

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

El 'servicio' que los objetos de acceso en el bucle será del tipo padre. Si sabe qué niño escribir estos tendrán antemano, sólo se puede acceder a la clase infantil de la siguiente manera:

from django.core.exceptions import ObjectDoesNotExist 

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

Si no conoce el tipo de niño de antemano, se pone un poco más complicado. Hay algunas soluciones hacky/messy, incluido un campo 'serviceType' en el modelo principal, pero una forma mejor, como mencionó Joe J, es utilizar un 'conjunto de preguntas de subclases'. La clase InheritanceManager de django-model-utils es probablemente la más fácil de usar. Lea la documentación here, es un pequeño código.

+0

Gracias por el detallado, lleno de código, respuesta. Con el de @Joe J, estoy bastante seguro de que me ayudará a lo largo del modelado de mi aplicación. Este sitio es excelente, sus usuarios también :) – pistache

+0

OK, esa fue una gran solución que dio aquí, especialmente el truco de InheritanceManager, y todo el paquete django-model-utils. Gracias de nuevo – pistache

6

Creo que uno de los enfoques que podría considerar es un "conjunto de preguntas de subclases". Básicamente, le permite consultar el modelo principal y devolverá las instancias de los modelos secundarios en el conjunto de respuestas resultante. Éste le permitirá hacer consultas como:

models.service.objects.all() 

y tienen que volver a resultados como el siguiente:

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

Para algunos ejemplos de cómo hacer esto, echa un vistazo a los enlaces de la entrada de blog vinculado a continuación.

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

Sin embargo, si se utiliza este método, no debe declarar su modelo de servicio tan abstracto como lo hace en el ejemplo. De acuerdo, introducirás una unión adicional, pero en general he encontrado que el conjunto de preguntas de subclases funciona bastante bien para devolver un conjunto mixto de objetos en un conjunto de consulta.

De todos modos, espero que esto ayude, Joe

+1

Muchas gracias, su respuesta, con la de @Voightkampff, me ayudó a comprender la herencia del modelo y me brindó una nueva forma de pensar en la estructura de datos de mi modelo. Por otro lado, esta fue mi primera pregunta en stackoverflow.com, y estoy muy contento con el sitio web, la interfaz, los usuarios, las respuestas y ahora me gustaría compartir mi conocimiento. :) – pistache

0

Si usted está buscando para las relaciones de clave externa genéricos debe comprobar el Django contenttypes framework (incorporada en Django). Los documentos explican bastante cómo usarlo y cómo trabajar con relaciones genéricas.

+1

Gracias, pero como ya he dicho, ya lo he comprobado, pero realmente no entendí ni pude asignar los ejemplos en Internet a mi caso de uso. – pistache

0

Un servicio real solo puede estar en un nodo, ¿verdad? En ese caso, cuando no se tiene un campo

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

en la clase service?