Django:获取抽象基类的所有实现

时间:2009-11-13 15:07:49

标签: django

我已经定义了一些看起来像这样的模型:

class ABClass(models.Model):
   #common attributes
   class Meta:
     abstract = True

class ConcreteClass1(ABClass):
   #implementation specific attributes

class ConcreteClass2(ABClass):
   #implementation specific attributes

class ModifiedConcreteClass1(ConcreteClass1):
   #implementation specific attributes

我有一个视图,它带有一个'type'参数,指定要显示的类的类型。如果'type = None',我想显示ABClass的所有后代(在本例中为ConcreteClass {1,2},ModifiedConcreteClass1)。我在文档中搜索过,似乎找不到这样做的方法,不需要维护ConcreteClasses列表。 ABClass.__subclasses__()似乎很有希望但是当我打电话时它会返回一个空列表。

目前我最好的策略是在models.py中创建一个CLASS_LIST变量,该变量在运行时使用inspect方法填充ABClass的所有后代。有没有办法使用django api做到这一点?

由于

5 个答案:

答案 0 :(得分:6)

如果'django.contrib.contenttypes'中有INSTALLED_APPS,则已维护ConcreteClasses列表。

我是这样做的:

class GetSubclassesMixin(object):
    @classmethod
    def get_subclasses(cls):
        content_types = ContentType.objects.filter(app_label=cls._meta.app_label)
        models = [ct.model_class() for ct in content_types]
        return [model for model in models
                if (model is not None and
                    issubclass(model, cls) and
                    model is not cls)]

class ABClass(GetSubclassesMixin, models.Model):
    pass

当您需要子类列表时,只需调用ABClass.get_subclasses()

我在具体的基类中使用它,但我认为没有理由不使用抽象的类。

即使您的具体子类位于不同的Django应用程序中,此方法仍然有效。但请注意,它们与基类具有相同的app_label,因此get_subclasses()中的代码可以找到它们。

答案 1 :(得分:5)

我没有看到当你在后端设置抽象True时Django做了什么,但我玩了这个,我发现这有效。请注意,它仅适用于Python 2.6

from abc import ABCMeta

class ABClass():
    __metaclass__ = ABCMeta

class ConcreteClass1(ABClass):
     pass

class ConcreteClass2(ABClass):
     pass

print ABClass.__subclasses__()

结果

[<class '__main__.ConcreteClass1'>, <class '__main__.ConcreteClass2'>]

如果不使用ABCMeta和__metaclass__,您将收到一个空列表。

您可以阅读有关here内容的详细说明。只有这个问题,我不确定它是否会影响你是我无法弄清楚为什么当我创建一个ABClass的实例时,它找不到子类。也许通过玩一下并阅读更多的文档,它可能会让你在某个地方。

让我知道这对你有什么用,因为我对真正的答案真的很好奇。

答案 2 :(得分:5)

看看这个类似的问题:

How do I access the child classes of an object in django without knowing the name of the child class?

我的解决方案基本上是这样的:

def get_children(self):
    rel_objs = self._meta.get_all_related_objects()
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]

虽然还有许多其他好的解决方案。您实际上可能想要基类的子项,而不仅仅是子类。如果没有,那里的答案可以作为您解决方案的动力。

答案 3 :(得分:1)

我能够使用__subclass__()方法,但不能使用abtract=True类。删除abstract=True会将另一个表添加到您的数据库中(并且我会猜测更多的开销),但是然后您可以按照您的描述获取其所有子类。

答案 4 :(得分:1)

我意识到这是一种丑陋的解决方案,但我之前已经这样做了:

class ABClass(models.Model):
   #common attributes
   is_ABClass = True
   class Meta:
     abstract = True

class ConcreteClass1(ABClass):
   #implementation specific attributes

class ConcreteClass2(ABClass):
   #implementation specific attributes

class ModifiedConcreteClass1(ConcreteClass1):
   #implementation specific attributes

def get_ABClasses():
   this = modules[__name__]
   return [getattr(this, attr) for attr in dir(this)
           if hasattr(getattr(this, attr), 'is_ABClass') and attr != 'ABClass']