[Sqlalchemy-tickets] Issue #3219: Intermediate __abstract__ inheriting from model doesn't transfer
Brought to you by:
zzzeek
|
From: David L. <iss...@bi...> - 2014-10-03 16:13:39
|
New issue 3219: Intermediate __abstract__ inheriting from model doesn't transfer model's attrs https://bitbucket.org/zzzeek/sqlalchemy/issue/3219/intermediate-__abstract__-inheriting-from David Lord: I have the following models: * `Device` * `DeviceSource` has a foreign key and relationship to `Device` * `LDAPDeviceSource` is a joined-table inheritance of `DeviceSource` In order to make defining new `DeviceSource` subclasses easier, I created `DeviceSourceMixin`, which inherits from `DeviceSource` and provides declared attrs for `__tablename__` and the foreign primary `id`. `DeviceSourceMixin` is `__abstract__` so that it doesn't create a table of its own. The issue is that this intermediate `__abstract__` seems to break the declarative model. The foreign key and relationship in `DeviceSource` do not get inherited by `LDAPDeviceSource` when subclassing `DeviceSourceMixin`. This code demonstrates the issue: ``` import sqlalchemy as sa from sqlalchemy.ext.declarative import declared_attr, as_declarative from sqlalchemy.orm import Session, relationship engine = sa.create_engine('sqlite:///:memory:', echo=True) session = Session(bind=engine) @as_declarative(bind=engine) class Base(object): @declared_attr def __tablename__(cls): return cls.__name__.lower() id = sa.Column(sa.Integer, primary_key=True) class Device(Base): pass class DeviceSource(Base): type = sa.Column(sa.String, nullable=False) device_id = sa.Column(sa.Integer, sa.ForeignKey(Device.id), nullable=False) device = relationship(Device, backref='sources') __mapper_args__ = { 'polymorphic_on': type } class DeviceSourceMixin(DeviceSource): __abstract__ = True @declared_attr def __tablename__(cls): return cls.__name__.lower() @declared_attr def id(cls): return sa.Column(sa.Integer, sa.ForeignKey(DeviceSource.id), primary_key=True) class LDAPDeviceSource(DeviceSourceMixin): name = sa.Column(sa.String, nullable=False) __mapper_args__ = { 'polymorphic_identity': 'ldap' } Base.metadata.create_all() d1 = Device() s1 = LDAPDeviceSource(device=d1, name='s1') session.add(s1) session.commit() ``` It produces the following error: ``` Traceback (most recent call last): File "/home/david/Projects/cedar/example2.py", line 62, in <module> s1 = LDAPDeviceSource(device=d1, name='s1') File "<string>", line 4, in __init__ File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/state.py", line 260, in _initialize_instance return manager.original_init(*mixed[1:], **kwargs) File "<string>", line 6, in __init__ File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 526, in _declarative_constructor setattr(self, k, kwargs[k]) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 226, in __set__ instance_dict(instance), value, None) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 812, in set value = self.fire_replace_event(state, dict_, value, old, initiator) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 832, in fire_replace_event state, value, previous, initiator or self._replace_token) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 1148, in emit_backref_from_scalar_set_event passive=PASSIVE_NO_FETCH) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 980, in append collection.append_with_event(value, initiator) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/collections.py", line 653, in append_with_event self._data()._sa_appender(item, _sa_initiator=initiator) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/collections.py", line 1047, in append item = __set(self, item, _sa_initiator) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/collections.py", line 1019, in __set item = executor.fire_append_event(item, _sa_initiator) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/collections.py", line 716, in fire_append_event item, initiator) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 929, in fire_append_event value = fn(state, value, initiator or self._append_token) File "/home/david/.virtualenvs/cedar/lib/python3.4/site-packages/sqlalchemy/orm/attributes.py", line 1157, in emit_backref_from_collection_append_event child_impl = child_state.manager[key].impl KeyError: 'device' ``` |