--- /dev/null
+.. change::
+ :tags: bug, orm, declarative
+ :tickets: 4124
+ :versions: 1.2.0b4
+
+ Fixed bug where a descriptor that is elsewhere a mapped column
+ or relationship within a hierarchy based on :class:`.AbstractConcreteBase`
+ would be referred towards during a refresh operation, causing an error
+ as the attribute is not mapped as a mapper property.
+ A similar issue can arise for other attributes like the "type" column
+ added by :class:`.AbstractConcreteBase` if the class fails to include
+ "concrete=True" in its mapper, however the check here should also
+ prevent that scenario from causing a problem.
result = False
+ # in the case of inheritance, particularly concrete and abstract
+ # concrete inheritance, the class manager might have some keys
+ # of attributes on the superclass that we didn't actually map.
+ # These could be mapped as "concrete, dont load" or could be completely
+ # exluded from the mapping and we know nothing about them. Filter them
+ # here to prevent them from coming through.
+ if attribute_names:
+ attribute_names = attribute_names.intersection(mapper.attrs.keys())
+
if mapper.inherits and not mapper.concrete:
# because we are using Core to produce a select() that we
# pass to the Query, we aren't calling setup() for mapped
eq_(sess.query(Manager).all(), [Manager(name='dogbert')])
eq_(sess.query(Boss).all(), [Boss(name='pointy haired')])
+ e1 = sess.query(Engineer).order_by(Engineer.name).first()
+ sess.expire(e1)
+ eq_(e1.name, 'dilbert')
+
def test_explicit(self):
engineers = Table(
'engineers', Base.metadata,
self._roundtrip(Employee, Manager, Engineer, Boss)
+ def test_abstract_concrete_extension_descriptor_refresh(self):
+ class Employee(AbstractConcreteBase, Base, fixtures.ComparableEntity):
+ @declared_attr
+ def name(cls):
+ return Column(String(50))
+
+ class Manager(Employee):
+ __tablename__ = 'manager'
+ employee_id = Column(Integer, primary_key=True,
+ test_needs_autoincrement=True)
+ paperwork = Column(String(10))
+ __mapper_args__ = {
+ 'polymorphic_identity': 'manager', 'concrete': True}
+
+ class Engineer(Employee):
+ __tablename__ = 'engineer'
+ employee_id = Column(Integer, primary_key=True,
+ test_needs_autoincrement=True)
+
+ @property
+ def paperwork(self):
+ return "p"
+
+ __mapper_args__ = {
+ 'polymorphic_identity': 'engineer', 'concrete': True}
+
+ Base.metadata.create_all()
+ sess = Session()
+ sess.add(Engineer(name='d'))
+ sess.commit()
+
+ # paperwork is excluded because there's a descritor; so it is
+ # not in the Engineers mapped properties at all, though is inside the
+ # class manager. Maybe it shouldn't be in the class manager either.
+ assert 'paperwork' in Engineer.__mapper__.class_manager
+ assert 'paperwork' not in Engineer.__mapper__.attrs.keys()
+
+ # type currently does get mapped, as a
+ # ConcreteInheritedProperty, which means, "ignore this thing inherited
+ # from the concrete base". if we didn't specify concrete=True, then
+ # this one gets stuck in the error condition also.
+ assert 'type' in Engineer.__mapper__.class_manager
+ assert 'type' in Engineer.__mapper__.attrs.keys()
+
+ e1 = sess.query(Engineer).first()
+ eq_(e1.name, 'd')
+ sess.expire(e1)
+ eq_(e1.name, 'd')
+
def test_concrete_extension(self):
class Employee(ConcreteBase, Base, fixtures.ComparableEntity):
__tablename__ = 'employee'