2008-12-08 16 views
11

Próbuję znaleźć rzeczywistą klasę obiektu django-model, przy użyciu dziedziczenia modelu.Jak znaleźć "klasę betonu" modelu django bazowego

Niektóre kodu do opisania problemu:

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

class Child_1(Base): 
    pass 

class Child_2(Base): 
    pass 

Jeśli tworzę różne przedmioty o dwóch klasach dziecka i tworzenia queryset zawierających je wszystkie:

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

Chcę ustalić, czy obiekt jest typu Child_1 lub Child_2 w metodzie base, mogę dostać się do obiektu podrzędnego przez o1.child_1 i o2.child_2, ale to zrewolucjonizuje wiedzę o klasach potomnych w klasie bazowej.

I mają pochodzić z następującego kodu:

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 

Ale czuje się kruche i nie jestem pewien, co się dzieje, gdy jeśli dziedziczą w kilku poziomach.

Odpowiedz

13

Django implementuje dziedziczenie modelu za pomocą OneToOneField między tabelą modelu nadrzędnego a tabelą modelu podrzędnego. Kiedy robisz Base.object.all(), Django wysyła zapytanie tylko do tabeli Base i nie ma możliwości poznania tabeli podrzędnej. Dlatego niestety nie można bezpośrednio przejść do instancji modelu podrzędnego bez dodatkowych zapytań.

Ten snippet pokazuje wspólną metodę dodawania pole ContentType do modelu podstawowego:

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) 

Można wtedy powiedzieć if Base.content_type.model_class() celu określenia rodzaju.

Here to kolejny fragment, który dodaje niestandardowego menedżera do miksu.

Jak widać, oba te rozwiązania mogą być niezwykle kosztowne. Jeśli masz dużą liczbę instancji, użycie metody as_leaf_class() będzie wymagać jednego zapytania dla każdego elementu.

Zamiast tego, jeśli masz znany zestaw modeli potomnych, po prostu zapytaj każdy model osobno i agreguj instancje w jedną listę.

+0

Dzięki, że rozwiązałem mój problem –

+0

Nie wiem, czy to dlatego, że jestem na Django 1.4, ale to nie działało dla mnie, dopóki nie użyłem 'if (nie self.content_type_id)' zamiast self.content_type. najwyraźniej Django próbował załadować self.content_type, co spowodowało 'DoesNotExist' –

+0

AFAIK, dokładnie to robi ten pakiet: https://django-polymorphic.readthedocs.org/en/latest/ Zwraca konkretną klasę potomną, nawet jeśli używasz BaseClass.objects.filter() – guettli

-2

Czuje się krucha, ponieważ tak jest. (Jest to przedruk odpowiedzi w innym kontekście: See C++ casting programmatically : can it be done ?)

Czytaj na temat polimorfizmu. Niemal każda sytuacja "dynamicznej obsady" jest przykładem na walkę o polimorfizm.

Niezależnie od decyzji, którą podejmujesz w dynamicznej obsadzie, już została podjęta. Po prostu przenieś prawdziwą pracę do podklas.

Pominąłeś najważniejszą część swojego przykładu. Przydatna, polimorficzna praca.

Kiedy powiedziałeś "chcę określić, czy obiekt jest typu Child_1, czy Child_2 ...", pominąłeś ", dzięki czemu obiekt może wykonać obiekt aMethod() w sposób unikalny dla każdej podklasy". Ta metoda jest użyteczna i powinna być po prostu metodą obu podklas.

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. 

Ta sama metoda, wiele implementacji. Nigdy nie ma potrzeby "identyfikowania typu w czasie wykonywania" ani określania konkretnej klasy.

+0

Ale on nie ma odwołania do instancji modelu dziecka. Menedżer Base.objects zwraca tylko instancje Base. –

+0

@Daniel: Zwykle przetwarzamy Child_1.objects.all() i Child_2.objects.all(); prawie nie ma wołania o związek obu podtypów dzieci. –

+0

Generalnie, ale to nie było pytanie. –

0

Cóż ... Mój problem był. W widoku miałem ten główny model, powiedzmy "Big_Model", a niektóre "Small_Model" były powiązane z "Big_Model". Więc kiedy chciałem odzyskać wszystkie "Small_Model" związane z pewną instancją "Big_Model" zrobiłem to ** _ set.all() rzeczy. Ale chodzi o to, że Small_Model ma klasy potomne i chciałem, w views.py, aby dowiedzieć się, z której klasy potomnej każda instancja Small_Model jest związana. Moją sztuczką było zdefiniowanie metod boolowskich w modelu Small_Model, takich jak is_child_1() i is_child_2(). A gdy jest to prawda, stosujemy rzeczywisty wskaźnik potomny zamiast wskaźnika Small_Model.

Ok ... Ów nie jest wystarczająco jasna, nadal nie mam zbyt wiele czasu, aby napisać dobry przykład, więc będę po prostu skopiować i wkleić moją sprawę tutaj:

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 
5

Wystarczy popatrzeć na InheritanceManager w django-model-utils - dołączenie go do modelu daje konkretne zajęcia z dzieckiem (przynajmniej na pierwszym poziomie):

from model_utils.managers import InheritanceManager 

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

# ... 

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

modelu utils wymaga Django 1.2 lub wyższa.

0

Nieco zmodyfikowana wersja what Daniel Naab proposed:

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 
Powiązane problemy