Come faccio ad accedere alle classi figlio di un oggetto in Django senza conoscere il nome della classe del bambino?

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

  •  06-09-2019
  •  | 
  •  

Domanda

In Django, quando si ha una classe genitore e molteplici classi figlie che ereditano da essa si farebbe normalmente accedere a un bambino attraverso parentclass.childclass1_set o parentclass.childclass2_set, ma cosa succede se non conosco il nome della classe bambino specifico voglio?

C'è un modo per ottenere gli oggetti relativi al genitore> direzione bambino senza conoscere il nome della classe del bambino?

È stato utile?

Soluzione

( Aggiorna : per Django 1.2 e successivi, che può seguire le query select_related attraverso relazioni OneToOneField inversa (e quindi verso il basso gerarchie di ereditarietà), c'è una tecnica migliore a disposizione, che non richiede il campo real_type aggiunto sul modello di genitore. E 'disponibile come InheritanceManager nel django-model-utils progetto.)

Il solito modo per farlo è quello di aggiungere un ForeignKey al contentType sul modello padre che memorizza il tipo di contenuto di una corretta "foglia" di classe. Senza questo, si può avere a che fare un certo numero di query su tabelle figlio per trovare l'istanza, a seconda di quanto è grande il vostro albero di ereditarietà è. Ecco come ho fatto in un unico progetto:

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

Questa è implementata come una classe base astratta per renderlo riutilizzabile; si potrebbe anche mettere questi metodi e l'FK direttamente sulla classe genitore nel vostro particolare gerarchia di ereditarietà.

Questa soluzione non funziona se non si è in grado di modificare il modello di genitore. In tal caso, si sta praticamente bloccato controllando tutte le sottoclassi manualmente.

Altri suggerimenti

In Python, dato un ( "new-style") di classe X, è possibile ottenere le sue sottoclassi (diretti) con X.__subclasses__(), che restituisce un elenco di oggetti di classe. (Se si desidera che "ulteriori discendenti", avrete anche di chiamare __subclasses__ su ciascuna delle sottoclassi diretti, ecc ecc - se hai bisogno di aiuto su come farlo in modo efficace in Python, basta chiedere).

Una volta che avete in qualche modo identificato una classe figlia di interesse (forse tutti loro, se si desidera che le istanze di tutte le sottoclassi bambini, ecc), getattr(parentclass,'%s_set' % childclass.__name__) dovrebbero aiutare (se il nome della classe bambino è 'foo', questo è proprio come l'accesso ai parentclass.foo_set - - Ne più ne meno). Anche in questo caso, se avete bisogno di chiarimenti o di esempi, si prega di chiedere!

La soluzione di Carl è un bene, ecco un modo per farlo manualmente se ci sono più classi figlie correlato:

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

Si utilizza una funzione di _meta, che non è garantito per essere stabile come Django si evolve, ma lo fa il trucco e può essere utilizzato on-the-fly, se necessario.

Si scopre che era questo ciò che ho davvero bisogno:

eredità Modello con tipo di contenuto e di eredità-aware gestore

che ha funzionato perfettamente per me. Grazie a tutti gli altri, però. Ho imparato molto semplicemente leggendo le vostre risposte!

È possibile utilizzare django-polimorfico per questo.

Permette di lanciare automaticamente classi derivate di nuovo al loro tipo effettivo. Esso fornisce anche il supporto Django admin, più efficiente la gestione di query SQL, e il modello di delega, inline e il supporto formset.

Il principio di base sembra essere reinventato molte volte (compresa .specific di ballerina, o gli esempi descritti in questo post). Ci vuole uno sforzo maggiore tuttavia, per assicurarsi che non si traduca in un numero N-query, o integrano perfettamente con le amministrazione, formsets / inline o applicazioni di terze parti.

Ecco la mia soluzione, ancora una volta utilizza _meta quindi non è garantito per essere stabile.

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

È possibile raggiungere questo alla ricerca di tutti i campi nel genitore che sono un esempio di django.db.models.fields.related.RelatedManager. Dal tuo esempio sembra che le classi figlio si sta parlando non sono sottoclassi. Giusto?

Un approccio alternativo utilizzando proxy può essere trovato in questo blog postare . Come le altre soluzioni, ha i suoi vantaggi e le passività, che sono molto ben messi, alla fine del post.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top