Comment puis-je accéder aux classes d'enfants d'un objet dans django sans connaître le nom de la classe des enfants?

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

  •  06-09-2019
  •  | 
  •  

Question

Dans Django, lorsque vous avez une classe parente et plusieurs classes d'enfants qui héritent de ce que vous auriez normalement accès à un enfant par parentclass.childclass1_set ou parentclass.childclass2_set, mais si je ne connais pas le nom de la classe enfant spécifique Je veux?

Y at-il un moyen d'obtenir les objets liés à la parent-> direction des enfants sans connaître le nom de la classe des enfants?

Était-ce utile?

La solution

( Mise à jour : Pour Django 1.2 et plus récent, qui peut suivre les requêtes select_related à travers les relations inverses de OneToOneField (et donc les hiérarchies d'héritage), il y a une meilleure technique disponible qui ne nécessite pas le champ real_type ajouté sur le modèle parent. Il est disponible InheritanceManager dans le django-modèle-utils projet.)

La façon de faire habituelle est d'ajouter un ForeignKey à CONTENTTYPE sur le modèle parent qui stocke le type de contenu de la classe appropriée « feuille ». Sans cela, vous pourriez avoir à faire un certain nombre de requêtes sur les tables enfants pour trouver l'instance, selon la taille de votre arbre d'héritage est. Voici comment je l'ai fait dans un projet:

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

Il est implémenté comme une classe de base abstraite pour le rendre réutilisable; vous pouvez aussi mettre ces méthodes et le FK directement sur la classe parente dans votre hiérarchie d'héritage particulier.

Cette solution ne fonctionnera pas si vous n'êtes pas en mesure de modifier le modèle de parent. Dans ce cas, vous êtes à peu près coincé vérifier tous les sous-classes manuellement.

Autres conseils

En Python, étant donné une classe X ( « nouveau style »), vous pouvez obtenir ses sous-classes (direct) avec X.__subclasses__(), qui retourne une liste d'objets de classe. (Si vous voulez « autres descendants », vous aurez également appeler __subclasses__ sur chacune des sous-classes directes, etc etc - si vous avez besoin d'aide sur la façon de le faire efficacement en Python, il suffit de demander).

Une fois que vous avez en quelque sorte identifié une classe d'enfants d'intérêt (peut-être tous, si vous voulez des cas de toutes les sous-classes d'enfants, etc.), getattr(parentclass,'%s_set' % childclass.__name__) devraient aider (si le nom de la classe enfant est 'foo', cela est tout comme l'accès à parentclass.foo_set - - Ni plus ni moins). Encore une fois, si vous avez besoin d'éclaircissements ou des exemples, s'il vous plaît demander!

La solution de Carl est un bon, voici une façon de le faire manuellement s'il y a plusieurs classes d'enfants associés:

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)]

Il utilise une fonction de _meta, qui n'est pas garanti d'être stable django évolue, mais il fait le tour et peut être utilisé à la volée en cas de besoin.

Il se trouve que ce que je avais vraiment besoin était le suivant:

héritage du modèle avec le gestionnaire de type de contenu et l'héritage conscient

Cela a parfaitement fonctionné pour moi. Merci à tout le monde, cependant. J'ai beaucoup appris en train de lire vos réponses!

Vous pouvez utiliser django-polymorphes pour cela.

Il permet de lancer automatiquement les classes dérivées à leur type réel. Il fournit également un soutien admin Django, le traitement SQL plus efficace des requêtes et le modèle de procuration, inline et le soutien formset.

Le principe de base semble être réinventé plusieurs fois (y compris le .specific de Bergeronnette, ou les exemples décrits dans cet article). Il faut plus d'efforts cependant, pour vous assurer qu'il ne donne pas lieu à un problème N-requête, ou bien intégrer avec l'administrateur, formsets / inline ou des applications tiers.

Voici ma solution, encore une fois, il utilise _meta donc n'est pas garanti d'être stable.

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

Vous pouvez réaliser cette recherche tous les champs du parent qui sont une instance de django.db.models.fields.related.RelatedManager. A partir de votre exemple, il semble que les classes d'enfants dont vous parlez ne sont pas des sous-classes. Droit?

Une autre approche utilisant les procurations se trouve dans ce blog poster . Comme les autres solutions, elle a ses avantages et passifs, qui sont très bien mis à la fin du poste.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top