Jordan Reiter
Jordan Reiter

Reputation: 21002

How do I create a class in Python that behaves like Django's abstract base class?

I'm using an existing Django library and I'm creating a class that I'd like to be abstract.

Let's say the class in question is BaseFoo.

Currently, the implementation has a base class BaseFoo.

Each time a class extending BaseFoo is created, a metaclass adds it to a list of classes. It explicitly doesn't add BaseFoo by doing a hard-coded test for the class name:

    if name not in ('BaseFoo',):
        class_list.append(new_cls)

I'd like to be able to write something like:

class MyBaseFoo(BaseFoo):
    class Meta:
        abstract = True

And have the metaclass skip all BaseFoo objects that are abstract. So I guess I'm wondering how Django does the same thing with models and whether there is an easy, elegant way to do this myself.

I've looked through some of the code Django uses to define models but I could use some pointers. Also if there is an even easier way of doing this without necessarily using class Meta I'm open to that too.

Note that I am not particularly thrilled by this solution:

class MyBaseFoo(BaseFoo):
    abstract = True

class ActualFoo1(MyBaseFoo):
    abstract = False

class ActualFoo2(MyBaseFoo):
    abstract = False

Upvotes: 2

Views: 657

Answers (1)

Martin v. Löwis
Martin v. Löwis

Reputation: 127467

There is no need to set abstract to False in concrete classes - it's sufficient to have this setting in the abstract classes only:

class_list = []
class CollectSubclasses(type):
    def __new__(cls, name, bases, attrs):
        abstract = False
        if attrs.get('abstract', False):
            abstract = True
            del attrs['abstract']
        res = super(CollectSubclasses, cls).__new__(cls, name, bases, attrs)
        if not abstract:
            class_list.append(res)
        return res

class BaseFoo(object):
    __metaclass__ = CollectSubclasses
    abstract = True

class Concrete1(BaseFoo):
    pass

class Abstract(BaseFoo):
    abstract = True

class Concrete2(Abstract):
    pass

print class_list

An alternative might be class decorators in Python 2.6, however, you would need to remove the class from the class_list in the @abstract decorator, since it has already been created when the decorator is called (and hence the metaclass was already invoked).

Upvotes: 3

Related Questions