Motivation
I think that SQLObject
is a fantastic
ORM.
However, one aspect
of its behaviour that fails to excite me is its way of managing
inheritance. You may refer to the documentation for details;
in brief,
SQLObject’s inheritance scheme assumes that
information distinguishing subclasses is stored in separate
tables, linked via foreign keys from the parent class table.
For various reasons, my own code contains data that represents
a few types in a hierarchy within the same table,
with subtypes denoted by flags: For instance, a
Spell is a (is_a) Skill, but
has a few additional properties; the skill table
contains a column is_spell, defaulting to
false.
What I wanted out of SQLObject was an inheritance scheme where
I could
- Specify that a
Spell is_a
Skill
get() and select()
Spell objects (and never get non-spell
Skills)
- get() and select() on the
Skill class, and retrieve either
Skill or Spell objects, as
appropriate
Features
SQLObjectInherits provides an eponymous decorator
that provides inheritance features for SQLObject
classes. The decorator is parameterised on an attribute (and
optionally, a value—normally and by default True)
to distinguish between subclasses.
The attribute will be used
intelligently to return the correct type from SQLObject
select() and get() calls, so that
iteration over a result set from a select() on
the parent may contain a mixture of parent and child class
instances.
Additionally, child classes are augmented to force and assume
the attribute value; e.g. when child instances are initialised,
the attribute is set automatically in __init__(),
and select() queries to the child class
automatically have the appropriate WHERE clause
injected.
Usage
# Sample usage of SQLObjectInherits
# (sample modified from development unit tests)
class Parent(SQLObject):
name = StringCol(length=32, alternateID=True, unique=True)
is_child = BoolCol(default=False)
Parent.createTable()
@SQLObjectInherits('is_child')
class Child(Parent):
class sqlmeta:
table = Parent.sqlmeta.table
def some_child_specific_stuff(self):
# ...
pass
some_bloke = Parent(name='Eddie')
some_lad = Child(name='Badcrumble')
# ...
for child in Child.select():
# All instances will be children
# ...
for person in Parent.select():
# Instances may be either Parent or Child instances
# ...
Restrictions
You must define sqlmeta
in all child classes. Because SQLObject works miscellaneous
magic during class construction time, the class is
tied to a table before the
SQLObjectInherits decorator does its work.
Each inheriting child class must have exactly one parent
SQLObject class. The behaviour of child classes
with multiple inheritance from classes extending
SQLObject is undefined.
Database requirements
You are perfectly free to define columns in child classes,
but of course if your schema contains columns that are
specific to some child class, and some rows will represent
instances of other classes, then these columns must allow
NULL values or contain sensible defaults.
Dependencies