2011-06-10 51 views
7

假设一组简单的继承模型类,像这样:如何从模型库到Django的派生类?

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”。有没有更好的方式来做到这一点,而不需要在基类中创建一个明确的“类型”字段?

回答

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(),也许忘了,让你在那里的两轮牛车。

+1

那么..最好看看django-polymorphic ;-) – vdboor 2012-07-10 12:48:34

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) 

你需要一些工厂来创建特定的类,所以你一定要正确保存STR(自我。)在CLASS_NAME

+0

是的,存储self.class_name是我试图避免... – slacy 2011-06-10 20:37:32

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类中定义它。

+1

“从...一个派生类的基类通常是程序中糟糕设计的标志。“胡说些什么。从基类转到派生类称为多态,是面向对象的基础。只是在这个特定的情况下,它没有黑客就无法工作。 – jwg 2014-08-26 17:42:21

+1

@jwg你读过我写的其余部分,还是只关注那个特定的句子?多态性在您拥有树中常见的方法/属性时起作用。如果您需要派生属性,那么您应该使用派生模型,而不是基础模型。 Django多表模型继承是多态性的一个很差的近似,并且在我看来,(几乎)所有的django继承应该用抽象基础来完成。 – 2014-08-27 03:26:34

+1

而且..就这么清楚。 “从基础班到派生班通常是程序设计不好的标志。” - 向下转换为派生类时。使用多态性动态分派到子类方法或属性是很好的。 – 2014-08-27 03:27:50

4

对于这个特定的问题,有django-polymorphic。它通过使用Django中的内容类型框架来存储派生表指向的模型ID。当您评估查询集时,它会将所有模型的特定类型上传。

您将获得:

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

好吧,我可以看到你可以使用管理器方法来获得派生类实例。作者从父模型实例到派生模型实例的问题如何? django-polymorphic是否也提供了解决方案? – eugene 2015-03-18 02:01:38

+1

是的,单个模型也有'get_real_instance()'方法。大多数时候你不需要它,因为经理已经给出派生模型。此外,它还通过在单个查询中获取同一类型的多个对象来避免N-query问题。 – vdboor 2015-03-20 12:41:10

0

您还可以的情况下,使用InheritanceQuerySetdjango-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