- changed status to closed
Problems with Update and Table Inheritance
Dev Setup
- Python 3.5
- SQLAlchemy 1.13
- OSX
I am a bit new to inheritance modeling for tables so it's quite possible this is not a bug but rather lack of understanding. I've also tried scanning through past issues and I don't think I've seen this addressed though this posting from google groups may be relevant
Goal
I'm trying to update a single column in the parent class (People
) while using the child class (Engineer
).
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))
Problem
The last update fails:
session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary'})
# Generated SQL
# UPDATE engineer SET name=?, updated=CURRENT_TIMESTAMP FROM people WHERE # people.name IS ?
As you can see the statement is trying to update rows in incorrect table, name
is in the parent 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.
I believe the the google google groups possibly explains why SQLAlchemy doesn't emit two update statements if I were to update kind
. One for the update to kind
and another for the onupdate
column updated
, correct?
Comments (1)
-
repo owner - Log in to comment
It would also need to know how to limit rows to those that match Engineer only, which for joined table inheritance in the general sense (meaning, not relying upon the discriminator column, which is not a requirement for joined inheritance in general) would mean it needs to JOIN on person/engineer, which is not supported by standard SQL UPDATE. While MySQL has a special syntax for this, and Postgresql supports a little bit of special syntax to help with this, there's not a generalized highly performant way to express UPDATE a JOIN of multiple tables, and the point of query.update() is one of performance optimization. Additionally, query(Engineer) implies you'd like to update values across multiple tables simultaneously, which also is impossible in SQL (only MySQL supports very non-standard way of achieving this), so emitting session.query(Engineer).update() in general does not describe adequate intent in light of the fact that UPDATE does not support updating a JOIN.
These limitations of query.update() are documented if you read all the way through to the end of: http://docs.sqlalchemy.org/en/latest/orm/query.html?highlight=query%20update#sqlalchemy.orm.query.Query.update