2011-03-19 118 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