Como faço para encontrar o "classe concreta" de um modelo de django baseclass

votos
11

Estou tentando encontrar a classe real de um django-modelo de objeto, quando se utiliza modelo de herança.

Algum código para descrever o problema:

class Base(models.model):
    def basemethod(self):
        ...

class Child_1(Base):
    pass

class Child_2(Base):
    pass

Se eu criar vários objetos de duas classes para crianças e criar um queryset contendo todos eles:

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

Eu quero determinar se o objeto é do tipo Child_1 ou Child_2 em basemethod, posso chegar ao objeto filho via o1.child_1 e o2.child_2 mas que reconquista conhecimento sobre os childclasses no baseclass.

Eu vim com o seguinte 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

Mas parece frágil e eu não tenho certeza do que acontece quando se eu herdar em mais níveis.

Publicado 08/12/2008 em 12:11
fonte usuário
Em outras línguas...                            


5 respostas

votos
13

Django implementa herança modelo com um OneToOneField entre a tabela do modelo pai e mesa do modelo infantil. Quando o fizer Base.object.all(), o Django está consultando apenas a tabela base, e por isso tem nenhuma maneira de saber o que a tabela filho é. Portanto, infelizmente, não é possível ir diretamente para a instância de modelo criança sem consultas adicionais.

Este fragmento mostra um método comum de adicionar um campo ContentType para o modelo de 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)

Você pode então dizer if Base.content_type.model_class()para determinar o tipo.

Aqui é outro trecho que adiciona um gerenciador customizado na mistura.

Como você pode ver, ambas as soluções têm o potencial de ser extremamente caro. Se você tem um grande número de casos, usando o método as_leaf_class () vai exigir uma consulta em cada item.

Em vez disso, se você tem um conjunto conhecido de modelos da criança, simplesmente consultar cada modelo separadamente e agregar as instâncias em uma única lista.

Respondeu 08/12/2008 em 14:05
fonte usuário

votos
5

Ter um olhar para InheritanceManager em django-modelo-utils - anexá-lo a um modelo dá-lhe as classes filhas de concreto (pelo menos no primeiro nível):

from model_utils.managers import InheritanceManager

class Base(models.Model):
    objects = InheritanceManager()

# ...

Base.objects.all().select_subclasses() # returns instances of child classes

-utils modelo requer Django 1.2 ou superior.

Respondeu 12/12/2011 em 18:59
fonte usuário

votos
0

Versão ligeiramente modificada que Daniel Naab proposta :

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
Respondeu 03/07/2013 em 09:56
fonte usuário

votos
0

Bem ... Meu problema era. Em uma visão, eu tinha esse modelo principal, vamos dizer "Big_Model" e houve alguns "Small_Model" relacionadas com "Big_Model". Então, quando eu queria recuperar todos "Small_Model" relacionado a uma determinada instância de "Big_Model" Eu fiz essas coisas ** _ set.all (). Mas o ponto é que Small_Model tem aulas para crianças e eu queria, em views.py, para obter qual classe criança era cada uma das instâncias Small_Model relacionadas com. Meu truque era definir métodos boolean em modelo Small_Model como is_child_1 () e is_child_2 (). E quando é verdade, você aplica a criança real ponteiro em vez do ponteiro Small_Model.

Ok ... Isso não é clara o suficiente, eu ainda não tenho muito tempo para escrever um bom exemplo, então eu vou apenas copiar e colar meu caso aqui:

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
Respondeu 29/05/2010 em 20:19
fonte usuário

votos
-2

Ela se sente frágil, pois é. (Esta é uma reprodução de uma resposta em um contexto diferente. Veja C ++ fundição programaticamente:? Pode ser feito )

Leia-se sobre polimorfismo. Quase todas as situações "elenco dinâmico" é um exemplo de polimorfismo lutando para ser implementado.

Qualquer decisão que você está fazendo no elenco dinâmica já foi feita. Apenas delegar o trabalho real para as subclasses.

Você deixou de fora a parte mais importante do seu exemplo. O, o trabalho polimórfica útil.

Quando você disse "Eu quero determinar se o objeto é do tipo Child_1 ou Child_2 ..." você deixou de fora o "para que eu possa fazer o objeto fazer aMethod()de uma forma que é exclusivo para cada subclasse". Esse método é o trabalho útil, e deve ser simplesmente um método de ambas as subclasses.

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.

Mesmo método, várias implementações. Nunca uma necessidade de "run-time tipo de identificação" ou determinar a classe concreta.

Respondeu 08/12/2008 em 12:23
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more