2011-03-19 117 views
9

我開發Django應用程序,我有這樣的模型結構多態性Django模型

class Animal(models.Model): 
    aul = models.ForeignKey(Aul) 
    age = models.IntegerField() 

    def __unicode__(self): 
     return u'Animal' 

class Sheep(Animal): 
    wool = models.IntegerField() 

    def __unicode__(self): 
     return u'Sheep' 

而且我通過animal_set到模板和輸出的每一個對象像這樣{{ animal }}。輸出動物,但是創造羊類型的對象,想用動物的羊的方法__unicode__

Django模型中的多態性是否工作?我找到了幾個答案,但是有些代碼片段可以在模型中編寫,但我對本機解決方案很感興趣。

+0

檢出[django-polymorphic](https://github.com/chrisglass/django_polymorphic),它是爲此用例而設計的。它也適用於通過外鍵,ManyToManyFields等獲取模型。 – vdboor 2013-05-21 14:15:09

+0

不要這樣做! :P請參閱以下內容:http://stackoverflow.com/a/20353347/539490 – AJP 2013-12-03 14:11:36

回答

3

您可能通過訪問{{animal.sheep}}獲得成功 - 模型繼承不是您想象的那樣,在將此類繼承「轉換」爲隱式OneToOneField關係的封面下有一個沉重的元類機制。

+0

不知道爲什麼這個答案downvoted,因爲它是準確的問題。但有點不完整,但仍然準確。 – 2014-06-03 14:12:38

1

有一個非常簡單的django應用程序django-polymorphic-models可以幫助你。它會爲您提供一個模型本身的downcast()方法,它將返回您的「子」對象,以及一個特殊的查詢集類來處理這些問題!

知道在基本模型的查詢集上使用select_related()也會得到子對象,這些子對象通過OneToOneField引用,這可能會有助於提高性能!

8

在寫作的時候,Django的最新版本是1.2

但是它需要一些額外的元素來工作。

您需要爲每個將調用其自己的自定義QuerySet對象的動物模型分配一個自定義models.Manager對象。

基本上,而不是返回Animal情況下(這是你),SubclassingQuerySet調用as_leaf_class()方法來檢查項目的模式是Animal與否 - 如果是,那麼就返回它,否則執行它的模型上下文搜索。而已。

#models.py 
from django.db import models 
from django.contrib.contenttypes.models import ContentType 
from django.db.models.query import QuerySet 


class SubclassingQuerySet(QuerySet): 
    def __getitem__(self, k): 
     result = super(SubclassingQuerySet, self).__getitem__(k) 
     if isinstance(result, models.Model): 
      return result.as_leaf_class() 
     return result 

    def __iter__(self): 
     for item in super(SubclassingQuerySet, self).__iter__(): 
      yield item.as_leaf_class() 


class AnimalManager(models.Manager): 
    def get_query_set(self): # Use get_queryset for Django >= 1.6 
     return SubclassingQuerySet(self.model) 


class Animal(models.Model): 
    name = models.CharField(max_length=100) 
    content_type = models.ForeignKey(ContentType, editable=False, null=True) 
    objects = AnimalManager() 

    def __unicode__(self): 
     return "Animal: %s" % (self.name) 

    def save(self, *args, **kwargs): 
     if not self.content_type: 
      self.content_type = ContentType.objects.get_for_model(self.__class__) 
     super(Animal, self).save(*args, **kwargs) 

    def as_leaf_class(self): 
     content_type = self.content_type 
     model = content_type.model_class() 
     if model == Animal: 
      return self 
     return model.objects.get(id=self.id) 


class Sheep(Animal): 
    wool = models.IntegerField() 
    objects = AnimalManager() 

    def __unicode__(self): 
     return 'Sheep: %s' % (self.name) 

測試:

>>> from animals.models import * 
>>> Animal.objects.all() 
[<Sheep: Sheep: White sheep>, <Animal: Animal: Dog>] 
>>> s, d = Animal.objects.all() 
>>> str(s) 
'Sheep: White sheep' 
>>> str(d) 
'Animal: Dog' 
>>> 
+1

這主要是什麼djanogo多態模型所做的,但它提供了一個更通用的功能.... – 2011-03-19 15:16:43

+0

我認爲as_leaf_class方法每次都會打到db。我所做的是直接使用self來設置類.__ class__ = model – Johan 2016-10-13 19:08:38

+0

@johan不要更改代碼。 2011年,在1.6之前的Django中,'get_query_set'是正確的。如果您想通知訪問者已有重命名,請在評論中進行。 – 2016-10-14 02:05:56

1

我會推薦使用Django代理模型,例如,如果你有這是由羊和馬子類的基本模型動物可以使用:

class Animal(models.Model): 
    pass 

class Horse(Animal): 
    class Meta(Animal.Meta): 
     proxy = True 

class Sheep(Animal): 
    class Meta(Animal.Meta): 
     proxy = True 

這不是what Proxy models are intended的,但除非你需要存儲模型的特定數據的好處,我不會建議使用Django多態性單獨的表格。如果你有一百匹馬的特定屬性,它們的默認值都存儲在數據庫中,然後只有兩匹馬對象,但有一百萬只羊,那麼你有一百萬行,每行都有一百個你不關心的馬特定值關於,但是這隻有在你沒有足夠的磁盤空間時才真正相關,這是不太可能的。當多態性運行良好時,它很好,但是當它不好時,這是一種痛苦。

+0

您建議父內(即單表)繼承(與所需的聯合表繼承相反),但您不知道實際模型是該示例還是僅示例。 STI和JTI之間存在學術爭論。除非有相反的說法,否則您應該幫助他有效地開發JTI。 – 2014-06-03 14:25:29