2011-06-10 24 views
7

Basit bir miras alınan Model sınıfları kümesini varsayarsak, bu gibi:Django'da bir model tabanından türetilmiş sınıfa nasıl geçilir?

class BaseObject(models.Model): 
    some_field = models.SomeField(...) 

class AwesomeObject(BaseObject): 
    awesome_field = models.AwesomeField(...) 

class ExcellentObject(BaseObject): 
    excellent_field = models.ExcellentField(...) 

ve buna benzer bir sorgu:

found_objects = BaseObject.objects.filter(some_field='bogus') 

Her found nesnesini almanın ve geri döndürmenin en iyi yolu nedir? Türetilmiş sınıf? Şu an kullandığım kod şunun gibidir:

for found in found_objects: 
    if hasattr(found, 'awesomeobject'): 
     ProcessAwesome(found.awesomeobject) 
    elif hasattr(found, 'excellentobject'): 
     ProcessExcellent(found.excellentobject): 

Ancak, bunun bir "hasattr" kötüye kullanımı olduğunu hissediyor. Temel sınıfta açık bir "tip" alanı oluşturmadan bunu yapmanın daha iyi bir yolu var mı?

cevap

2

Bu, bildiğim en iyi yol. Ne yazık ki, miras bu konuda biraz aksaktır. Birden fazla tablo devralma temel olarak alt model ile çocuğun eklediği ek alanlar arasındaki birebir ilişkidir ve bu nedenle hasattr hilesi çalışır. Bunların her birini ebeveyn modelinize bir OneToOneField özniteliği olarak düşünebilirsiniz. Bu şekilde düşündüğünüzde, Django'nun hangi çocuğun döneceğini veya bir çocuğa geri döneceğini bile bilme yolu yoktur, bu nedenle bu mantığı kendiniz halletmelisiniz:

Ebeveyn gibi bir yöntem oluşturma eğilimindeyim nitelikleri aracılığıyla basitçe döngüleri ve açılan birini verir get_child,: en az sonra

class BaseObject(models.Model): 
    some_field = models.SomeField(...) 

    def get_child(self): 
     if hasattr(self, 'awesomeobject'): 
      return ProcessAwesome(found.awesomeobject) 
     elif hasattr(self, 'excellentobject'): 
      return ProcessExcellent(found.excellentobject): 
     else: 
      return None 

, sadece found.get_child() çağırabilir ve belki orada alır hackery unutun.

+1

Hayır .. Django-polymorphic ;-) 'ye daha iyi bakın ;-) – vdboor

2

Introspection kullanıyorum; doğru bir türetilmiş sınıfa Temel sınıftan gidiyor sınıfAdı

+0

Evet, self.class_name dosyasını saklamaktan kaçınmaya çalıştığım şey ... – slacy

3

yılında str (öz. sınıf) kaydetmek emin böylece

class Base(models.Model): 
[ we have some unique 'key' attribute ] 
class_name = models.CharField(..., editable=False) 

def get_base(self): 
    if self.__class__ == Base: 
     return self 
    # if we are not an instance of Base we 'go up' 
    return Base.objects.get(key=self.key) 

def get_specific(self): 
    if self.__class__ != Base: 
     return self 
    # if we are an instance of Base we find the specific class 
    class_type = getattr(sys.modules["project.app.models"], 
     self.class_name) 
    return class_type.objects.get(key=self.key) 

Genellikle bir işaretidir belirli sınıfları oluşturmak için bazı fabrika gerek Bir programda kötü tasarım. Önerdiğiniz yöntem, hasattr kullanarak, ciddi bir sorun olabilir. Ben size göstereceğim:

# defined in some open source library 
class MyObject(object): 
    def what_is_derived(self): 
     if hasattr(self, 'derived1'): 
      return 'derived1' 
     elif hasattr(self, 'derived2'): 
      return 'derived2' 
     else: 
      return 'base' 

sınıfları Derived1 ve Derived2 aynı kitaplığında tanımlanan farz edelim. Şimdi, MyObject'un özelliklerini kullanmak istiyorsunuz, böylece kendi kodunuzdan çıkarsınız.

# defined in your own code 
class MyBetterObject(MyObject): 
    pass 

better_object = MyBetterObject() 
better_object.what_is_derived() # prints 'base' 

Polimorfizmin bütün noktası, temel sınıf değişmek zorunda kalmadan birçok türetilmiş sınıfa sahip olmanızdır. Temel sınıfı, tüm türetilmiş sınıfların farkında olarak yaparak, böyle bir sınıfın yararlılığını ciddi ölçüde azaltırsınız. Temel sınıfı değiştirmeden türetilmiş bir sınıf oluşturamazsınız.

İster türetilmiş bir sınıfla çalışmak istersiniz, ya da belirli bir sınıfın ne olduğu ve ihtiyacınız olan şey temel sınıfın özellikleri/yöntemleridir. Tüm OOP dillerinde aynıdır. Türetilen sınıfın ne olduğunu bulmak için olanaklar vardır, ancak genellikle kötü bir fikirdir.Bir Django modelleri açısından bakıldığında

, ben genellikle bu şekilde devralma kullanın: Django modelleri ile

class Address(models.Model): 
    # fields... 

class Person(Address): 
    # fields... 

class Business(Address): 
    # fields... 

Address.objects.all() # find all addresses for whatever reason 
Person.objects.all() # im only interested in people 
Business.objects.all() # need to work with businesses 

# need to show all addresses in a postcode, and what type of address they are? 
businesses = Business.objects.filter(postcode='90210') 
people = Person.objects.filter(postcode='90210') 
# use the address properties on both 

Derinden yuvalanmış miras zincirleri tuhaftır. Çoğu durumda da oldukça gereksizdirler. Temel sınıfınızı hasattr kontrolleriyle kirletmek yerine, böyle bir şey istendiğinde gerekli türetilmiş sınıfları sorgulayabilen bir yardımcı yöntem tanımlayın. Sadece onu Base sınıfında tanımlamayın.

+1

" türetilmiş bir sınıfa temel bir sınıf, genellikle bir programda kötü tasarımın belirtisidir. " Ne saçma. Temel sınıftan türetilmiş bir sınıfa gitmek polimorfizm olarak adlandırılır ve nesne yönelimi için temeldir. Sadece bu durumda, o bir hack olmadan çalışmaz. – jwg

+1

@jwg Yazdıklarımın geri kalanını okudunuz mu, ya da sadece o cümleye odaklandınız mı? Polymorphism, ağaç genelinde ortak yöntemler/öznitelikleriniz olduğunda çalışır. Türetilmiş özniteliklere ihtiyacınız varsa, o zaman temel modelle değil, türetilmiş modelle çalışmalısınız. Django çok masalı model kalıtım, polimorfizmin zayıf bir yaklaşımıdır ve bana göre, (neredeyse) tüm django mirası, soyut üslerle yapılmalıdır. –

+1

Ve .. sadece bu yüzden açık. "Bir temel sınıftan türetilmiş bir sınıfa geçmek genellikle bir programda kötü tasarımın bir işaretidir." - türetilmiş bir sınıfa düştüğünde. Bir alt sınıf yöntemine veya özniteliğine dinamik olarak göndermek için polimorfizm kullanılması iyidir. –

4

Bu özel sorun için, django-polymorphic var. Türetilmiş tablonun işaret ettiği model kimliğini saklamak için Django'daki içerik türü çerçevesini kullanarak çalışır. Sorgulamayı değerlendirdiğinizde, tüm modelleri kendi türlerine göre artacak.

Sen alırsınız:

>>> BaseProject.objects.all() 
[ <AwesomeObject>, <ExcellentObject>, <BaseObject>, <AwesomeObject> ] 
+0

Tamam Türetilmiş sınıf örnekleri almak için yönetici yöntemlerini kullanabileceğinizi görebiliyorum. Yazarın, bir üst model örneğinden türetilmiş bir model örneğine gitme sorunu nasıl? Django-polimorfik bunun için bir çözüm sunuyor mu? – eugene

+1

Evet, bireysel modelin bir 'get_real_instance()' yöntemi de vardır. Çoğu zaman ihtiyacınız yok çünkü yönetici zaten türetilmiş modelleri veriyor. Ayrıca, aynı türden birden çok nesneyi tek bir sorguda getirerek N-sorgu sorununu da önler. – vdboor

0

Ayrıca açıkça böyle, etkilemeye sorgular hangi devlet istediğiniz durumda django-model-utils den InheritanceQuerySet kullanabilirsiniz:

from model_utils.managers import InheritanceQuerySet 

class UserManager([...]): 

    def get_queryset(self): 
     return InheritanceQuerySet(self.model).select_subclasses() 

(https://stackoverflow.com/a/25108201 gelen kodu)

İlgili konular