¿Cómo encuentro la “clase concreta” de una clase de base de modelo de django?

Estoy tratando de encontrar la clase real de un objeto de modelo django, cuando uso la herencia de modelo.

Algún código para describir el problema:

class Base(models.model): def basemethod(self): ... class Child_1(Base): pass class Child_2(Base): pass 

Si creo varios objetos de las dos clases secundarias y creo un queryset que los contiene a todos:

 Child_1().save() Child_2().save() (o1, o2) = Base.objects.all() 

Quiero determinar si el objeto es de tipo Child_1 o Child_2 en basemethod, puedo acceder al objeto hijo a través de o1.child_1 y o2.child_2 pero eso reconquista el conocimiento sobre las clases de niño en la clase básica.

Se me ha ocurrido el siguiente código:

 def concrete_instance(self): instance = None for subclass in self._meta.get_all_related_objects(): acc_name = subclass.get_accessor_name() try: instance = self.__getattribute__(acc_name) return instance except Exception, e: pass 

Pero se siente frágil y no estoy seguro de qué pasará si heredaré más niveles.

Django implementa la herencia del modelo con OneToOneField entre la tabla del modelo principal y la tabla del modelo secundario. Cuando lo hace Base.object.all() , Django solo consulta la tabla Base, por lo que no tiene forma de saber cuál es la tabla secundaria. Por lo tanto, desafortunadamente, no es posible ir directamente a la instancia del modelo secundario sin consultas adicionales.

Este fragmento muestra un método común para agregar un campo ContentType al modelo base:

 from django.contrib.contenttypes.models import ContentType class Base(models.Model): content_type = models.ForeignKey(ContentType,editable=False,null=True) def save(self): if(not self.content_type): self.content_type = ContentType.objects.get_for_model(self.__class__) self.save_base() def as_leaf_class(self): content_type = self.content_type model = content_type.model_class() if(model == Base): return self return model.objects.get(id=self.id) 

Luego puede decir if Base.content_type.model_class() para determinar el tipo.

Aquí hay otro fragmento que agrega un administrador personalizado a la mezcla.

Como puede ver, ambas soluciones tienen el potencial de ser extremadamente caras. Si tiene un gran número de instancias, el uso del método as_leaf_class () requerirá una consulta en cada elemento.

En cambio, si tiene un conjunto conocido de modelos secundarios, simplemente consulte cada modelo por separado y agregue las instancias en una lista.

Eche un vistazo a InheritanceManager en django-model-utils : adjuntarlo a un modelo le ofrece clases secundarias concretas (al menos en el primer nivel):

 from model_utils.managers import InheritanceManager class Base(models.Model): objects = InheritanceManager() # ... Base.objects.all().select_subclasses() # returns instances of child classes 

model-utils requiere Django 1.2 o superior.

Bueno … Mi problema era. En una vista, tuve este modelo principal, digamos “Big_Model” y hubo algunos “Small_Model” relacionados con “Big_Model”. Así que cuando quise recuperar todos los “Small_Model” relacionados con una determinada instancia de “Big_Model” hice eso ** _ set.all () cosas. Pero el punto es que Small_Model tiene clases secundarias y, en views.py, quería saber a qué clase secundaria pertenecía cada una de las instancias de Small_Model. Mi truco fue definir métodos booleanos en el modelo Small_Model como is_child_1 () y is_child_2 (). Y cuando es cierto, se aplica el puntero secundario real en lugar del puntero Small_Model.

Ok … Eso no está lo suficientemente claro, aún no tengo mucho tiempo para escribir un buen ejemplo, así que copiaré y pegaré mi caso aquí:

 class Cache(models.Model): valor = models.DecimalField(max_digits=9, decimal_places=2, blank= True, null= True) evento=models.ForeignKey(Evento) def __unicode__(self): return u'%s: %s' % (self.evento, self.valor) class Meta: verbose_name='Cachê' verbose_name_plural='Cachês' def is_cb(self): try: self.cache_bilheteria return True except self.DoesNotExist: return False def is_co(self): try: self.cache_outro return True except self.DoesNotExist: return False 

Versión ligeramente modificada de lo que propuso Daniel Naab :

 from django.contrib.contenttypes.models import ContentType from django.db import models def ParentClass(models.Model): superclass = models.CharField(max_length = 255, blank = True) def save(self, *args, **kwargs): if not self.superclass: self.superclass = ContentType.objects.get_for_model(self.__class__) super(ParentClass, self).save(*args, **kwargs) def getChild(self): s = getattr(self, self.superclass) if hasattr(s, 'pk'): return s else: return None class Child1(ParentClass): pass class Child2(ParentClass): pass 

Se siente frágil porque lo es. (Esta es una reimpresión de una respuesta en un contexto diferente. Vea la conversión de C ++ programáticamente: ¿se puede hacer? )

Lea sobre el polymorphism. Casi cada situación de “reparto dynamic” es un ejemplo de polymorphism que lucha por ser implementado.

Cualquier decisión que tomes en el reparto dynamic ya se ha tomado. Solo delegue el trabajo real a las subclases.

Dejaste la parte más importante de tu ejemplo. El trabajo útil, polimorfo.

Cuando dijo “Quiero determinar si el objeto es de tipo Child_1 o Child_2 …” omitió “para que pueda hacer que el objeto haga un aMethod() de una manera que sea única para cada subclase”. Ese método es el trabajo útil, y debería ser simplemente un método de ambas subclases.

 class Base(models.model): def aMethod(self): # base class implementation. class Child_1(Base): def aMethod(self): # Child_1 override of base class behavior. class Child_2(Base): def aMethod(self): supert( Child_2, self ).aMethod() # Invoke the base class version # Child_2 extension to base class behavior. 

Mismo método, implementaciones múltiples. Nunca es necesario “identificar el tipo de tiempo de ejecución” o determinar la clase concreta.