Thursday, August 7, 2008

Parameterised classes in Python

Recently, when working with Django, I wanted to create a class that would automatically render instances of a model as an editable HTML table. I used a Django-esque approach to defining the table columns, such that it was similar to Model and Form classes:

class ProductTable(Table):
    _model = Product
    name = TextWidget()
    description = TextWidget()
    size = DropDownWidget()


The underscore in _model is there to prevent a name collision with the column names, but it's a bit ugly. If I was strictly following the Django approach, I probably would have added an inner class:

class ProductTable(Table):
    class Meta:
        _model = Product

    name = TextWidget()
    description = TextWidget()
    size = DropDownWidget()


But that's a lot of extra noise. What I really wanted was something more like template classes in C++.

template <class Model>
class Table : public TableBase {
  // ...
}

class ProductTable : public Table<Product> {
  // ...
}


If Python supported class decorators, I could have just put @model(Product) before the class definition. The approach I chose is to inherit from a class dynamically generated by a function that takes the parameter I need, so my ProductTable class now becomes:

class ProductTable(Table(Product)):
    name = TextWidget()
    description = TextWidget()
    size = DropDownWidget()


Much nicer! Now it pretty much reads as 'a ProductTable is a kind of Table (a Product one)', which makes sense. To support this, I have a Table function that looks like this:

def Table(model):
    class TableDynamicBase(TableBase):
        _model = model
    return TableDynamicBase


TableBase contains whatever was in the original Table base class (which in my case was some unrelated metaclass magic).

Wednesday, August 6, 2008

Introduction to Django

I haven't had any kind of personal web site for ages, but I recently wrote an article, Introduction to Django, for Digital Web magazine, so I thought I'd put a quick something up here as a point of contact.