¿Cómo accedo a las clases hijas de un objeto en Django sin saber el nombre de la clase hija?

StackOverflow https://stackoverflow.com/questions/929029

  •  06-09-2019
  •  | 
  •  

Pregunta

En Django, cuando se tiene una clase padre y múltiples clases hijas que heredan de ella que normalmente se accede a través de un niño o parentclass.childclass1_set parentclass.childclass2_set, pero lo que si no sé el nombre de la clase hija específica Quiero?

¿Hay una manera de conseguir los objetos relacionados en el padre-> dirección niño sin saber el nombre de la clase de niños?

¿Fue útil?

Solución

( Actualizar : Para Django 1.2 y superior, que pueda seguir consultas select_related a través de las relaciones OneToOneField inversa (y por lo tanto las jerarquías de herencia), hay una mejor técnica disponible que no requiere el campo real_type añadido en el modelo de los padres. está disponible como InheritanceManager en el django-modelo-utils proyecto.)

La forma habitual de hacer esto es añadir un ForeignKey a CONTENTTYPE en el modelo de padres que almacena el tipo de contenido de la clase adecuada "hoja". Sin esto, es posible que tenga que hacer un buen número de consultas en tablas secundarias para encontrar la instancia, en función del tamaño de su árbol de herencia es. Así es como yo lo hice en un proyecto:

from django.contrib.contenttypes.models import ContentType
from django.db import models

class InheritanceCastModel(models.Model):
    """
    An abstract base class that provides a ``real_type`` FK to ContentType.

    For use in trees of inherited models, to be able to downcast
    parent instances to their child types.

    """
    real_type = models.ForeignKey(ContentType, editable=False)

    def save(self, *args, **kwargs):
        if not self._state.adding:
            self.real_type = self._get_real_type()
        super(InheritanceCastModel, self).save(*args, **kwargs)

    def _get_real_type(self):
        return ContentType.objects.get_for_model(type(self))

    def cast(self):
        return self.real_type.get_object_for_this_type(pk=self.pk)

    class Meta:
        abstract = True

Esto se implementa como una clase base abstracta para que sea reutilizable; También puede poner estos métodos y el FK directamente en la clase padre en su jerarquía de herencia particular.

Esta solución no funcionará si usted no es capaz de modificar el modelo padre. En ese caso, que está bastante atascado comprobar todas las subclases de forma manual.

Otros consejos

En Python, dada una ( "nuevo estilo") clase X, usted puede conseguir sus subclases (directos) con X.__subclasses__(), que devuelve una lista de objetos de la clase. (Si quieres más "descendientes", también tendrá que llamar __subclasses__ en cada una de las subclases directas, etc, etc - si necesita ayuda sobre cómo hacerlo de manera eficaz en Python, sólo hay que preguntar).

Una vez que haya identificado de alguna manera una clase hija de interés (tal vez todos ellos, si quieres casos de todas las subclases de niños, etc.), getattr(parentclass,'%s_set' % childclass.__name__) deben ayudar (si el nombre de la clase de niño es 'foo', esto es como acceder a parentclass.foo_set - - ni mas ni menos). Una vez más, si necesita aclaración o ejemplos, por favor pregunte!

La solución de Carl es una buena idea, aquí hay una manera de hacerlo de forma manual si hay varias clases hijas relacionados:

def get_children(self):
    rel_objs = self._meta.get_all_related_objects()
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]

Se utiliza una función de _meta, lo que no se garantiza que sea estable como Django evoluciona, pero hace el truco y se puede utilizar en la marcha si es necesario.

Resulta que lo que realmente necesitaba era la siguiente:

herencia Modelo con tipo de contenido y de herencia-conscientes gerente

Eso ha funcionado perfectamente para mí. Gracias a todos los demás, sin embargo. He aprendido mucho sólo leer sus respuestas!

Puede utilizar django-polimórfica para eso.

Permite a emitir automáticamente las clases derivadas de vuelta a su tipo real. También proporciona soporte Django de administración, manejo de consulta SQL más eficiente y modelo de poder, inline y el apoyo conjunto de formularios.

El principio básico parece ser reinventado muchas veces (incluyendo .specific de lavandera, o los ejemplos descritos en este post). Se necesita más esfuerzo sin embargo, para asegurarse de que no se traduce en un problema N-consulta o integrar muy bien con el administrador, formsets / inline o aplicaciones de terceros.

Aquí está mi solución, una vez más se utiliza _meta por lo que no se garantiza que sea estable.

class Animal(models.model):
    name = models.CharField()
    number_legs = models.IntegerField()
    ...

    def get_child_animal(self):
        child_animal = None
        for r in self._meta.get_all_related_objects():
            if r.field.name == 'animal_ptr':
                child_animal = getattr(self, r.get_accessor_name())
        if not child_animal:
            raise Exception("No subclass, you shouldn't create Animals directly")
        return child_animal

class Dog(Animal):
    ...

for a in Animal.objects.all():
    a.get_child_animal() # returns the dog (or whatever) instance

Esto se puede conseguir en busca de todos los campos de los padres que son una instancia de django.db.models.fields.related.RelatedManager. De su ejemplo parece que las clases hijas que estamos hablando no son subclases. ¿Verdad?

Un enfoque alternativo utilizando proxies se puede encontrar en este blog publicar . Al igual que las otras soluciones, tiene sus ventajas y desventajas, que son muy bien ponen en el final de la entrada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top