rolisz's site

Single table inheritance in Camelot

Recently, while working on a class project, I had a small problem with in­her­i­tance in Camelot, an excellent framework for rapid ap­pli­ca­tion de­vel­op­ment in Python.

In the ap­pli­ca­tion we needed to be able to define different kinds of projects, with similar needs, but which were each available only to some user groups. Single table in­her­i­tance sounds like a natural fit for this.

I couldn't find anything in the Camelot doc­u­men­ta­tion about this, but I figured that since it uses SQLAlchemy for its models, I could try the example given there:

 class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(50))
    engineer_info = Column(String(50))
    type = Column(String(20))

    __mapper_args__ = {
        'polymorphic_on':type,
        'polymorphic_identity':'employee'
    }

class Manager(Employee):
    __mapper_args__ = {
        'polymorphic_identity':'manager'
    }

class Engineer(Employee):
    __mapper_args__ = {
        'polymorphic_identity':'engineer'
    }

All is nice and well, except it won't work in Camelot, if we are using the Entity instead of Base, because it gives the following error:

sqlalchemy.exc.ArgumentError: Can't find any foreign key relationships between 'employee' and 'engineer'.{lang="python"}

Googling for this wasn't too helpful (and this is why I'm writing this post), so I asked on Stack­Over­flow and on the Camelot mailing list and finally got an answer from Erik, the developer of Camelot.

He said that if Camelot doesn't see tablename defined, it tries to deduce it au­to­mat­i­cal­ly (and creates something like "engineer"), and then of course, SQLAlchemy thinks it has a multitable in­her­i­tance, but doesn't have a foreign key to connect the two tables. So the solution is simple: add tablename = None to each inherited table.

class Manager(Employee):
    __tablename__ = None

    __mapper_args__ = {
        'polymorphic_identity':'manager'
    }

class Engineer(Employee):
    __tablename__ = None

    __mapper_args__ = {
        'polymorphic_identity':'engineer'
    }

Voila. Hope it helps.