如何从模型库到Django中的派生类?

时间:2011-06-10 18:56:58

标签: django django-models

假设一组简单的继承Model类,如下所示:

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

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

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

和一个如下所示的查询:

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

获取每个found对象并将其转换为派生类的最佳方法是什么?我现在使用的代码是这样的:

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

但是,感觉这是滥用“hasattr”。如果没有在基类上创建显式的“类型”字段,有没有更好的方法呢?

5 个答案:

答案 0 :(得分:4)

对于此特定问题,有django-polymorphic。它通过使用Django中的内容类型框架来存储派生表指向的模型ID。在评估查询集时,它将向所有模型转发其特定类型。

你会得到:

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

答案 1 :(得分:3)

从基类到派生类通常是程序中设计不良的标志。您使用hasattr建议的方法可能是一个严重的问题。我会告诉你:

# 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'

让我们假装在同一个库中定义了类Derived1Derived2。现在,您想要使用MyObject的功能,因此您可以在自己的代码中派生它。

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

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

多态性的全部意义在于,您可以拥有许多派生类,而基类不必更改。通过使基类知道所有派生类,您将严重降低此类的有用性。如果不更改基类,则无法创建派生类。

您要么使用派生类,要么您不关心特定类是什么,您需要的只是基类的属性/方法。所有OOP语言都是一样的。有设施可以找出派生类是什么,但通常这是一个坏主意。

从django模型的角度来看,我通常以这样的方式使用继承:

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

使用django模型深度嵌套的继承链很尴尬。在大多数情况下,它们也非常不必要。不要使用hasattr检查来污染您的基类,而是定义一个辅助方法,如果要调用这样的东西,它就能够查询所需的派生类。只是不要在Base类上定义它。

答案 2 :(得分:2)

这是我所知道的最佳方式。不幸的是,遗传在这方面有点笨拙。多表继承基本上只是父模型和子项添加的额外字段之间的一对一关系,这就是hasattr技巧有效的原因。您可以将每个属性视为父模型上的OneToOneField属性。当你这么想的时候,Django无法知道要返回哪个孩子,甚至不知道要给孩子一个孩子,所以你必须自己处理这个逻辑:

我倾向于在父类上创建一个方法,例如get_child,它只是循环遍历属性并返回弹出的方法:

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

至少那时,你可以打电话给found.get_child(),也许会忘记让你在那里的hackery。

答案 3 :(得分:2)

我使用内省;

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)

您需要一些工厂来创建特定的类,因此您确保在class_name中正确保存str(self。 class

答案 4 :(得分:0)

您还可以使用InheritanceQuerySet中的django-model-utils,以防您明确说明要影响哪些查询,例如:

from model_utils.managers import InheritanceQuerySet

class UserManager([...]):

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

(来自https://stackoverflow.com/a/25108201的代码)