Inconsistent behavior for polymorphic queries

Issue #2750 resolved
Marc Schlaich created an issue

This is my test case:

from sqlalchemy import create_engine, Column, ForeignKey, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Model(Base):
    __tablename__ = 'model'
    id = Column(Integer, primary_key=True)
    label = Column(String)

    mtype = Column(Integer, nullable=False)
    __mapper_args__ = {'polymorphic_on': mtype}


class SubModel(Model):
    __tablename__ = 'submodel'
    id = Column(Integer, ForeignKey('model.id'), primary_key=True)
    __mapper_args__ = {'polymorphic_identity': 1}


def main():
    engine = create_engine('sqlite:///:memory:')
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()

    m = Model(mtype=1)
    m.label = 'x'
    session.add(m)
    session.commit()
    m_id = m.id

    session = Session()
    m = session.query(Model).get(m_id)
    m.label = 'y'
    session.commit()
    print m.id

This yields the following error:

Traceback (most recent call last):
  File "sqla_test.py", line 44, in <module>
    main()
  File "sqla_test.py", line 40, in main
    print m.id
  File "D:\Projekte\HACS\packages\sqlalchemy\orm\attributes.py", line 316, in __get__
    return self.impl.get(instance_state(instance), dict_)
  File "D:\Projekte\HACS\packages\sqlalchemy\orm\attributes.py", line 611, in get
    value = callable_(passive)
  File "D:\Projekte\HACS\packages\sqlalchemy\orm\state.py", line 375, in __call__
    self.manager.deferred_scalar_loader(self, toload)
  File "D:\Projekte\HACS\packages\sqlalchemy\orm\loading.py", line 606, in load_scalar_attributes
    raise orm_exc.ObjectDeletedError(state)
sqlalchemy.orm.exc.ObjectDeletedError: Instance '<SubModel at 0x2f0eeb0>' has been deleted, or its row is otherwise not
present.

I have already investigated and can explain the behavior: the query returns actually a SubModel instance because of mtype == 1. After the commit the object is expired so a new query is issued which joins model against submodel. But an entry in the submodel table was never created.

This behavior is really inconsistent. SQLAlchemy should throw an error on the query or on the insert. Alternatively, it might fix the insert automatically by creating an corresponding entry to submodel.

Besides, if you issue session.query(SubModel).get(m_id) you get the same error message, which is really misleading.

Comments (5)

  1. Mike Bayer repo owner

    It shouldn't let you persist on a joined inh with an incompatible type, but note that we do support sending this type for the single table use case. Ultimately the type has to be reconciled against the tables corresponding to it.

  2. Log in to comment