Wie greife ich auf die untergeordneten Klassen eines Objektes in django, ohne den Namen des Kindes Klasse zu kennen?

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

  •  06-09-2019
  •  | 
  •  

Frage

In Django, wenn Sie eine übergeordnete Klasse und mehr untergeordneten Klassen, die von ihm erben würden Sie normalerweise ein Kind durch parentclass.childclass1_set oder parentclass.childclass2_set zugreifen, aber was ist, wenn ich den Namen der jeweiligen Kind Klasse nicht kennen ich will?

Gibt es eine Möglichkeit die entsprechenden Objekte in der Eltern- zu bekommen> Kind Richtung, ohne die Kind Klassennamen zu wissen?

War es hilfreich?

Lösung

( Aktualisieren : Für Django 1.2 und neuere, die select_related Abfragen über Reverse OneToOneField Beziehungen folgen kann (und damit Vererbungshierarchien nach unten), gibt es eine bessere Technik zur Verfügung, die das zusätzlichen real_type Feld nicht erforderlich auf dem übergeordneten Modell. Es ist erhältlich als InheritanceManager in der django-model-utils Projekt.)

Der üblicher Weg, dies zu tun, ist ein ForeignKey hinzuzufügen auf dem übergeordnete Modell CONTENT, die den Inhaltstyp der richtigen „Blatt“ Klasse speichert. Ohne diese können Sie eine ganze Reihe von Anfragen auf geordneten Tabellen zu tun haben, um die Instanz zu finden, je nachdem, wie groß Ihr Vererbungsbaum ist. Hier ist, wie ich tat es in einem Projekt:

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

Dies ist als eine abstrakte Basisklasse implementiert, um es wiederverwendbar zu machen; Sie auch diese Methoden und die FK direkt auf die übergeordneten Klasse in Ihrer speziellen Vererbungshierarchie setzen könnten.

Diese Lösung funktioniert nicht, wenn Sie nicht in der Lage sind das übergeordnete Modell zu ändern. In diesem Fall bist du ziemlich viel stecken Überprüfung aller Subklassen von Hand.

Andere Tipps

In Python, da eine ( "new-style") Klasse X, können Sie den (direkten) Subklassen mit X.__subclasses__() erhalten, die eine Liste von Klassenobjekten zurückzugibt. (Wenn Sie „weitere Nachkommen“ wollen, werden Sie auch __subclasses__ auf jedem der direkten Unterklassen rufen müssen, etc etc - wenn Sie Hilfe benötigen, wie das effektiv in Python zu tun, fragen Sie einfach).

Sobald Sie haben irgendwie ein Kind Klasse von Interesse identifiziert (vielleicht alle von ihnen, wenn Sie Instanzen aller Kind-Subklassen wollen, usw.), sollte getattr(parentclass,'%s_set' % childclass.__name__) helfen (wenn der Name des Kindes Klasse 'foo' ist, das ist nur wie parentclass.foo_set Zugriff - - nicht mehr und nicht weniger). wenn Sie Klärung oder Beispiele Auch hier müssen, fragen Sie bitte!

Carl-Lösung ist gut, hier kann man es manuell tun, wenn es mehrere verwandte Kind Klassen:

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

Es verwendet eine Funktion aus _meta, die nicht garantiert ist, stabil zu sein, wie django sich entwickelt, aber es funktioniert der Trick und kann on-the-fly verwendet werden, wenn es nötig ist.

Es stellt sich heraus, dass das, was ich wirklich dies erforderlich war:

Modell Vererbung mit Inhaltstyp und Vererbung-aware-Manager

Das ist perfekt für mich gearbeitet. Vielen Dank an alle anderen, though. Ich habe gelernt, eine Menge Ihre Antworten lesen!

Sie können mit django-polymorpher dafür.

Es ermöglicht automatisch abgeleitete Klassen zurückgeworfen auf ihren tatsächlichen Typen. Es bietet auch Django Admin-Unterstützung, effizientere SQL-Abfrage Handhabung und Proxy-Modell, inlines und formset Unterstützung.

Das Grundprinzip scheint oft neu erfunden werden (einschließlich Bachstelze des .specific oder die Beispiele in diesem Beitrag beschrieben). Es dauert jedoch mehr Mühe, um sicherzustellen, dass es nicht in einem N-Abfrage Problem führt, oder integriert gut mit den admin, Formularsätzen / inlines oder Anwendungen von Drittanbietern.

Hier ist meine Lösung, wieder verwendet es _meta so nicht stabil sein, ist gewährleistet.

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

Sie können dies erreichen, für alle Felder in der übergeordneten suchen, der eine Instanz von django.db.models.fields.related.RelatedManager sind. Von Ihrem Beispiel scheint es, dass die Kind-Klassen Sie sprechen, sind nicht Subklassen. Richtig?

Ein alternativer Ansatz Proxies können in Blog finden Post . Wie die anderen Lösungen, hat es seine Vorteile und Verbindlichkeiten, die sich sehr gut in das Ende der Post setzen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top