Como faço para acessar as crianças classes de um objeto em django sem saber o nome da classe criança?

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

  •  06-09-2019
  •  | 
  •  

Pergunta

Em Django, quando você tem uma classe pai e múltiplas classes filhas que herdam dele você normalmente aceder a uma criança através parentclass.childclass1_set ou parentclass.childclass2_set, mas o que se eu não sei o nome da classe criança específica eu quero?

Existe uma maneira de obter os objetos relacionados no pai-> direção criança sem saber o nome da classe criança?

Foi útil?

Solução

( Atualizar : Para Django 1.2 e mais recente, que pode seguir consultas select_related através de relações OneToOneField reversa (e, portanto, para baixo hierarquias de herança), há uma melhor técnica disponível que não requer o campo real_type acrescentou no modelo de pai. Ele está disponível como InheritanceManager na django-model-utils projeto.)

A maneira usual de fazer isso é adicionar um ForeignKey para ContentType no modelo de pai que armazena o tipo de conteúdo da classe adequada "folha". Sem isso, você pode ter que fazer um grande número de consultas em tabelas filho para encontrar o exemplo, dependendo de como grande sua árvore de herança é. Aqui está como eu fiz isso em um projeto:

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

Esta é implementado como uma classe base abstrata para torná-lo reutilizável; Você também pode colocar esses métodos eo FK diretamente sobre a classe pai em sua hierarquia de herança particular.

Esta solução não vai funcionar se você não é capaz de modificar o modelo de pai. Nesse caso, você está muito presa a verificação de todos os subclasses manualmente.

Outras dicas

Em Python, dado um ( "novo estilo") classe X, você pode obter suas subclasses (diretas) com X.__subclasses__(), que retorna uma lista de objetos de classe. (Se você quiser "mais descendentes", você também vai ter que chamar __subclasses__ em cada uma das subclasses diretas, etc etc - Se precisar de ajuda sobre como fazer isso de forma eficaz em Python, basta perguntar).

Depois de ter alguma forma identificada uma classe filha de interesse (talvez todos eles, se você quiser instâncias de todas as subclasses de crianças, etc), getattr(parentclass,'%s_set' % childclass.__name__) deve ajudar (se o nome da classe criança é 'foo', este é apenas como acesso parentclass.foo_set - - nem mais nem menos). Novamente, se você precisar de esclarecimentos ou exemplos, por favor, pergunte!

A solução de Carl é um bom, aqui está uma maneira de fazê-lo manualmente, se existem várias classes filho 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)]

Ele usa uma função fora do _meta, o que não é garantido para ser estável como Django evolui, mas ele faz o truque e pode ser usado on-the-fly, se necessário.

Acontece que o que eu realmente precisava era esta:

herança Modelo com tipo de conteúdo e gerente herança-aware

Isso tem funcionado perfeitamente para mim. Graças a todos os outros, no entanto. Eu aprendi muito apenas lendo suas respostas!

Você pode usar django-polimórfica para isso.

Ele permite lançar automaticamente as classes derivadas de volta para seus tipo real. Ele também fornece suporte Django admin, manipulação de consulta SQL mais eficiente, e modelo proxy, inlines e apoio formset.

O princípio básico parece ser reinventada muitas vezes (incluindo .specific do Wagtail, ou os exemplos descritos neste post). É preciso mais esforço, no entanto, para se certificar de que não resulta em um problema N-consulta, ou integrar bem com o administrador, formsets / inlines ou aplicativos de terceiros.

Aqui está a minha solução, mais uma vez ele usa _meta por isso não é garantido para ser estável.

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

Você pode conseguir isto olhando para todos os campos do pai que são uma instância de django.db.models.fields.related.RelatedManager. A partir do seu exemplo, parece que as crianças classes que você está falando não são subclasses. Certo?

Uma abordagem alternativa utilizando proxies podem ser encontradas no neste blog postar . Como as outras soluções, tem seus benefícios e passivos, que são muito bem colocados no final do post.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top