2009-12-11 15 views
5

Estoy creando una aplicación Django que usa algo de herencia en su modelo, principalmente porque necesito asignar todo un UUID y una referencia para saber qué clase era. Aquí hay una versión simplificada de la clase base:Herencia y funciones de fábrica en Python y Django

class BaseElement(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

class ChildElement(BaseElement): 
    somefield = models.CharField(max_length=255) 

me gustaría asegurarse de que objmodule, objclass y UUID se ajustan automáticamente. Aprendí de this post que es una mala idea escribir mi propio constructor y que es mejor escribir una función de fábrica. Así que ahora mi BaseElement y ChildElement se ve así:

class BaseElement(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

    def set_defaults(self): 
     self.objmodule = unicode(self.__class__.__module__) 
     self.objclass = unicode(self.__class__.__name__) 
     self.uuid = unicode(uuid4()) 

class ChildElement(BaseElement): 
    somefield = models.CharField(max_length=255) 

    @staticmethod 
    def create(*args, **kwargs): 
     ce = ChildElement(*args, **kwargs) 
     ce.set_defaults() 
     return ce 

Esto funciona. Puedo llamar al ChildElement.create(somefield="foo") y obtendré un objeto apropiado con los campos uuid, objmodule y objclass establecidos correctos. Sin embargo, a medida que avanzo y creo más clases como ChildElement2 y ChildElement3, me doy cuenta de que estoy insertando exactamente la misma función de fábrica estática. Esto me irrita porque la duplicación de código es mala.

Con los métodos normales, simplemente inserta la función de fábrica create en BaseElement, sin embargo, no puedo hacer eso porque no tengo un identificador (porque no se ha creado aún) para obtener información sobre la clase del objeto que invocó el método.

¿Hay una manera de que pueda migrar esta fábrica en la clase BaseElement por lo que no tiene que duplicar este código en todas partes y todavía tienen que por lo que ajusta automáticamente los valores de uuid, objmodule y objclass?

Respuesta

7

Si comete un create()@classmethod en lugar de @staticmethod, tendrá acceso a la clase de objeto, que se puede utilizar en lugar de referirse a ella por su nombre:

@classmethod 
def create(cls, *args, **kwargs): 
    obj = cls(*args, **kwargs) 
    obj.set_defaults() 
    return obj 

esto es ahora genérica y puede ir en la clase base en lugar de cada clase infantil.

+1

Eso es exactamente lo que estaba buscando. Gracias por recordarme sobre '@ classmethod'. No es algo que use muy a menudo. – Pridkett

+0

En Python, rara vez hay un buen caso de uso para '@ static method.-Es probable que deba usarlo con menos frecuencia, si es que lo hace alguna vez. A menudo, si algo no es un método @ @ classmethod, es más apropiado como una función simple de nivel superior en un módulo. –

2

Creo que es mejor que hayas reemplazado en tu BaseElement. Luego, al guardar, podría establecer esos campos. Sería algo así como:

class MyBase(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, 
     default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

    def save(self): 
     if not self.id: 
      self.objmodule = unicode(self.__class__.__module__) 
      self.objclass = unicode(self.__class__.__name__) 
      self.uuid = unicode(uuid4()) 
     super(self.__class__.__base__, self).save() 

class InheritedFromBase(MyBase): 
    new_field = models.CharField(max_length=100) 

Probé con eso y parecía hacer lo que estaba buscando. Pude crear un objeto "InheritedFromBase" que tenía los campos que necesitaba, sin mucha duplicación de código.

+0

+1: Simplemente anule 'guardar' casi siempre es lo correcto. –

+1

Eso está cerca, y puede llegar a ser aceptable, pero no es exactamente lo que necesito. Ahora mismo creo algunos objetos en el servidor y los serializo en un cliente para su uso en una aplicación AJAXy. El cliente puede modificar atributos y luego guardar los objetos. Con su configuración, no obtendrían 'uuid',' objmodule' y 'objclass' asignados hasta que se guarden. Como utilizo 'uuid' en el cliente, probablemente necesite agregar alguna lógica adicional al servidor, o hacer que el cliente lo asigne. Tan cerca, pero me gustaría encontrar un camino sin invocar 'save' primero. Sin embargo, puedo tratar si este es el camino correcto. – Pridkett