DISCLAIMER: You should keep @AlexMartelli's answer as chosen. Because he's right and just because... well... He's THE Alex Martelli, with his own Wikipedia page and all ('nuf said) but I was working on my answer before I saw his, and I'm going to post it, anyway. If anything, just as a little example that might clarify how SqlAlchemy deals with Joined Table inheritance.
What SqlAlchemy does when you query a (Manager) is going to the manager table, grab all the attributes, also JOINing the "parent" table (employee) through the id's ForeignKey and bring the rest into your instance, so you can "trick" SqlAlchemy by altering that relationship "manually" (the echo=True argument to the engine is very useful to see what SqlAlchemy is doing)
IMHO, what I'm copying below is extremely discouraged, and I would never pull something like this into real production, but... I really like SqlAlchemy, and I like to share the very little I know of its abilities with whomever wants to listen (and for the people who don't want to listen, I chase them down and shout my opinions at them :-D )
There it goes (some comments inline):
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import CreateSchema
from sqlalchemy import event
Base = declarative_base()
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
engineer_name = Column(String(30))
__mapper_args__ = {
'polymorphic_identity':'engineer',
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
manager_name = Column(String(30))
__mapper_args__ = {
'polymorphic_identity':'manager',
}
if __name__ == '__main__':
engine = create_engine("mysql://root:password@localhost/tests?charset=utf8",
echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
employee = Employee(name="Pepe")
manager = Manager(name="Foobar", manager_name="Mr. Baz")
session.add_all([employee, manager])
session.commit()
"""
table tests.employee looks like:
| id | name | type |
| 1 | Pepe | employee |
| 2 | Foobar | manager |
table tests.manager looks like:
| id | manager_name |
| 2 | Mr. Baz |
"""
to_promote = session.query(Employee).filter_by(name="Pepe").first()
managers = [manager.name for manager in session.query(Manager).all()]
session.close()
print ("As of now (point 1), got %s managers: %s"
% (len(managers), managers))
print "Employee to_promote: %s" % to_promote.name
connection = engine.connect()
connection.execute(
"START TRANSACTION;"
"UPDATE employee SET employee.type='manager' WHERE employee.id={0};"
"INSERT INTO manager (id, manager_name) VALUES ({0}, 'New Pepe');"
"COMMIT;".format(to_promote.id)
)
connection.close()
"""
table tests.employee looks like:
| id | name | type |
| 1 | Pepe | manager |
| 2 | Foobar | manager |
table tests.manager looks like:
| id | manager_name |
| 1 | New Pepe |
| 2 | Mr. Baz |
"""
session = Session()
managers = [manager.name for manager in session.query(Manager).all()]
print ("As of now (point 2), got %s managers: %s"
% (len(managers), managers))
This outputs:
2015-03-21 13:10:41,787 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2015-03-21 13:10:41,788 INFO sqlalchemy.engine.base.Engine INSERT INTO employee (name, type) VALUES (%s, %s)
2015-03-21 13:10:41,788 INFO sqlalchemy.engine.base.Engine ('Pepe', 'employee')
2015-03-21 13:10:41,789 INFO sqlalchemy.engine.base.Engine INSERT INTO employee (name, type) VALUES (%s, %s)
2015-03-21 13:10:41,789 INFO sqlalchemy.engine.base.Engine ('Foobar', 'manager')
2015-03-21 13:10:41,790 INFO sqlalchemy.engine.base.Engine INSERT INTO manager (id, manager_name) VALUES (%s, %s)
2015-03-21 13:10:41,790 INFO sqlalchemy.engine.base.Engine (2L, 'Mr. Baz')
2015-03-21 13:10:41,791 INFO sqlalchemy.engine.base.Engine COMMIT
2015-03-21 13:10:41,827 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2015-03-21 13:10:41,828 INFO sqlalchemy.engine.base.Engine SELECT employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type
FROM employee
WHERE employee.name = %s
LIMIT %s
2015-03-21 13:10:41,828 INFO sqlalchemy.engine.base.Engine ('Pepe', 1)
2015-03-21 13:10:41,829 INFO sqlalchemy.engine.base.Engine SELECT manager.id AS manager_id, employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type, manager.manager_name AS manager_manager_name
FROM employee INNER JOIN manager ON employee.id = manager.id
2015-03-21 13:10:41,830 INFO sqlalchemy.engine.base.Engine ()
2015-03-21 13:10:41,830 INFO sqlalchemy.engine.base.Engine ROLLBACK
As of now (point 1), got 1 managers: [u'Foobar']
Employee to_promote: Pepe
2015-03-21 13:10:41,831 INFO sqlalchemy.engine.base.Engine START TRANSACTION;UPDATE employee SET employee.type='manager' WHERE employee.id=1;INSERT INTO manager (id, manager_name) VALUES (1, 'New Pepe');COMMIT;
2015-03-21 13:10:41,831 INFO sqlalchemy.engine.base.Engine ()
2015-03-21 13:10:41,868 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2015-03-21 13:10:41,869 INFO sqlalchemy.engine.base.Engine SELECT manager.id AS manager_id, employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type, manager.manager_name AS manager_manager_name
FROM employee INNER JOIN manager ON employee.id = manager.id
2015-03-21 13:10:41,869 INFO sqlalchemy.engine.base.Engine ()
As of now (point 2), got 2 managers: [u'Pepe', u'Foobar']