繁体   English   中英

Django模型中的多态性

[英]Polymorphism in Django models

我正在开发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 }}这样的每个对象。 它输出Animal ,但我创建了羊类型的对象,并且想要使用羊的__unicode__方法而不是动物。

多态在Django模型中有效吗? 我找到了几个答案,但是有一些代码片段可以在模型中编写,但我对本机解决方案很感兴趣。

在撰写本文时,Django最新版本为1.2

但它需要一些额外的元素才能工作。

您需要为每个动物模型分配一个自定义models.Manager对象,它将调用自己的自定义QuerySet对象。

基本上,不是返回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'
>>> 

您可能通过访问{{animal.sheep}}获得成功 - 模型继承不是您想象的,封面下有一个重型元类机制将这种继承“转换”为隐式的OneToOneField关系。

我建议使用Django代理模型,例如,如果您有基本模型Animal,它将由Sheep和Horse子类化,您将使用:

class Animal(models.Model):
    pass

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

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

这不是代理模型的目的 ,但我不建议使用Django多态,除非您需要在单独的表中存储模型特定数据的好处。 如果你有一百个特定的属性,都有默认值存储在数据库中,然后只有2个马对象,但有一百万只羊,你就有一百万行,每一行有一百匹特定的值,你不在乎但是,如果你没有足够的磁盘空间,这只是真正相关的,这是不太可能的。 当多态性运行良好时,它很好,但是当它不是时,它就是一种痛苦。

有一个非常简单的django应用程序,称为django-polymorphic模型 ,可以帮助你。 它将为您提供模型本身的downcast()方法,它将返回您的“子”对象,以及一个特殊的查询集类来处理这些问题!

知道在基础模型的查询集上使用select_related()也将获得通过OneToOneField引用的子对象也是非常有用的,这有时可以提升性能!

你应该检查这个答案: https//stackoverflow.com/a/929982/684253

它提出的解决方案类似于使用django-polymorphic模型,@ lazerscience已经提到过。 但是我会说django-model-utils比django-polymorphic有更好的文档记录,而且库更容易使用。 查看“继承管理器”下的自述文件: https//github.com/carljm/django-model-utils/#readme

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM