This pattern doesnt entirely make sense - the "citizen_type" and
"employee_type" columns seem superfluous and redundant against each
other, since we really can't load Engineer rows without querying all
three tables. In that sense it takes on all the limitations of
concrete table inheritance, which doesnt use a "type" column at the
table level.
Also, a key aspect of SQLA's polymorphic loading capability is that a
mapper is aware of all of its possible subtypes. If multiple
inheritance is part of that, the geometry of "what are all my
subtypes?" becomes a more chaotic. We'd have to join to every table
in the whole hierarchy to identify the type. To be fair I think this
is a behavior that Hibernate supports but they only support it for
single inheritance (and they also boast of how difficult it was to
implement). SQLA's usual notion of "primary key" with respect to
joined table inheritance wouldn't work here either (engineer's PK is
either (x, y) or (x, y, z), employee and citizen are just (x)),
suggesting again a more "concrete" notion - you need to select from
the subclass table in order to locate the object, and the primary key
itself does not provide enough information to select the appropriate
subclass table.
The standard patterns for "multiple" inheritance in SQL are listed at http://www.agiledata.org/essays/mappingObjects.html#MappingMultipleInheritance
. There you'll find examples of concrete, single, and joined table
"multiple" inheritance.
You can certainly map to any of the hierarchies indicated in that
article, but you wouldn't be able to take advantage of SQLA's
"polymorphic" capabilities, which are designed to only handle single
inheritance. You'd really want to make your Engineer(Employee,
Citizen) class and just map it to
engineers.join(citizens).join(employees). That would get your schema
going, just without SQLA having any awareness of the "inheritance"
portion of it, and is extremely similar to a plain concrete setup,
which is pretty much all you'd get anyway without the ability to load
polymorphically.
> For my application, this pattern is important (the above example is
> only an example of the pattern, I'm not really modeling employees and
> citizens) and I was wondering if anyone had any suggestions as to how
> to go about implementing this functionality, which I'm planning on
> doing.
if you mean "implementing" within SQLAlchemy itself such that its core
notion of inheritance is modified to support multiple base classes
spread across multiple tables, this would be an enormously difficult
feature to implement. For polymorphic loading, at the very least
SQLA would need to lose its dependency on "discriminator" columns and
learn to just look for row presence in a table as evidence of
belonging to a certain type (that alone is not necessarily a bad
thing, as Hibernate does this too).
It would also need to learn to create joins to other tables
corresponding to horizontal and vertical relationships, and be able to
guess the type of a row based on a complicated equation of row
presence. All of the ambigousness introduced by multiple inheritance,
like diamond patterns and such would also have to be dealt with. So
I'm not really sure that even with the best of efforts, multiple
inheritance could ever be nearly as transparent as single
inheritance. Beyond the effort level to implement, I'd be very
concerned about the complexity it would introduce to SQLA's
internals. The use case itself seems exceedingly rare. While a
recipe that "gets the job done" is entirely fine in this case, I'm
fairly skeptical of functionality like this as a core feature.
the examples around ruby-on-rails are for one2many/many2one:
http://wiki.rubyonrails.org/rails/pages/UnderstandingPolymorphicAssociations
sqlalchemy/examples/poly_assoc/
i have a many2many version,
http://dbcook.svn.sourceforge.net/viewvc/dbcook/trunk/dbcook/usage/polymassoc.py
the idea: the assoc.table points to the value and to all the possible
owners (via foreign keys), with only one of the owners set-up for
each record. this can probably be extended to point to multiple types
of values too.
ciao
svil
>
> This pattern doesnt entirely make sense - the "citizen_type" and
> "employee_type" columns seem superfluous and redundant against each
> other, since we really can't load Engineer rows without querying all
> three tables. In that sense it takes on all the limitations of
> concrete table inheritance, which doesnt use a "type" column at the
> table level.
>
after a night's sleep, let me backtrack a bit. having
discriminiator columns in all superclass tables would probably still
be effective in the way we use discriminiator columns right now. the
mapper would basically have to use one or the other in the case where
more than one is available (like, querying subclasses of Engineer).
You would want to share the primary key column across all three tables
though (i.e. foreign key in the subclass table, like the link I
mentioned) so that the primary key takes on the same form no matter
what class you're looking at - that helps inheritance a great deal
since its one of the assumptions SQLA makes.
The change to SA's internals would still be pretty heavy and its hard
to say what kinds of roadblocks would appear when developing such a
feature.
> Michael, what would the mapper function look like if it were to map
> Engineer(Employee, Citizen) to
> engineers.join(citizens).join(employees). What argument of the mapper
> would that join condition be in? I think concrete inheritance might be
> the way to go about things, at the cost of the nice polymorphic
> loading features.
that would just be the ordinary "table" argument. The join
conditions are within the join() calls themselves. mapper(Engineer,
engineers.join(citizens,...).join(employees, ...)) . I dont think
you can even say "concrete=True" here unless there were an "inherits"
argument, in which case you'd have to just pick a superclass out of
the two.... it would be better to not use the inherits argument at all
though (pretty sure SQLA won't complain).