I'm building an inheritance table schema like the following:
Spec Code
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
discriminator = Column('type', String(50))
updated = Column(DateTime, server_default=func.now(), onupdate=func.now())
name = Column(String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
class Engineer(Person):
__mapper_args__ = {'polymorphic_identity': 'engineer'}
start_date = Column(DateTime)
class Manager(Person):
__mapper_args__ = {'polymorphic_identity': 'manager'}
start_date = Column(DateTime)
UPDATED (WORKING) CODE
import os
import sys
from sqlalchemy import Column, create_engine, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base
try:
os.remove('test.db')
except FileNotFoundError:
pass
engine = create_engine('sqlite:///test.db', echo=True)
Session = sessionmaker(engine)
Base = declarative_base()
class People(Base):
__tablename__ = 'people'
discriminator = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
id = Column(Integer, primary_key=True)
name = Column(String(50))
updated = Column(DateTime, server_default=func.now(), onupdate=func.now())
class Engineer(People):
__tablename__ = 'engineer'
__mapper_args__ = {'polymorphic_identity': 'engineer'}
id = Column(Integer, ForeignKey('people.id'), primary_key=True)
kind = Column(String(100), nullable=True)
Base.metadata.create_all(engine)
session = Session()
e = Engineer()
e.name = 'Mike'
session.add(e)
session.flush()
session.commit()
# works when updating the object
e.name = "Doug"
session.add(e)
session.commit()
# works using the base class for the query
count = session.query(People).filter(
People.name.is_('Doug')).update({People.name: 'James'})
# fails when using the derived class
count = session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary'})
session.commit()
print("Count: {}".format(count))
Note: this is slightly modified example from sql docs
If I try to update the name for Engineer two things should happen.
- update statement to the People table on column
name - automatic trigger of update to the
updatedcolumn on the People table
For now, i'd like to focus on number 1. Things like the example below (as also documented in the full code example) will result in invalid SQL
session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary'})
I believe the above generates the following:
UPDATE engineer SET name=?, updated=CURRENT_TIMESTAMP FROM people WHERE people.name IS ?
Again, this is invalid. The statement is trying to update rows in incorrect table. name is in the base table.
I'm a little unclear about how inheritance tables should work but it seems like updates should work transparently with the derived object. Meaning, when I update Engineer.name querying against the Engineer object SQLAlchemy should know to update the People table. To complicate things a bit more, what happens if I try to update columns which exist in two tables
session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary', Engineer.start_date: '1997-01-01'})
I suspect SQLAlchemy will not issue two update statements.