From: Mike Bayer Date: Sat, 7 May 2011 15:30:24 +0000 (-0400) Subject: - Fixed bug where determination of "self referential" X-Git-Tag: rel_0_6_8~15 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b3d7bd4e3eb52fbd4fba6ed97ee67748274ac712;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug where determination of "self referential" relationship would fail with no workaround for joined-inh subclass related to itself, or joined-inh subclass related to a subclass of that with no cols in the sub-sub class in the join condition. [ticket:2149] --- diff --git a/CHANGES b/CHANGES index a63656245f..25da1e7d89 100644 --- a/CHANGES +++ b/CHANGES @@ -30,6 +30,13 @@ CHANGES mapper 'X' does not map this column" to reference the correct mapper. [ticket:2163]. + - Fixed bug where determination of "self referential" + relationship would fail with no workaround + for joined-inh subclass related to itself, + or joined-inh subclass related to a subclass + of that with no cols in the sub-sub class + in the join condition. [ticket:2149] + - sql - Fixed bug whereby if FetchedValue was passed to column server_onupdate, it would not @@ -94,12 +101,6 @@ CHANGES inside to the outside query if from_self() were used. [ticket:2130]. - - It is an error to call query.get() when the - given entity is not a single, full class - entity or mapper (i.e. a column). This is - a deprecation warning in 0.6.8. - [ticket:2144] - - sql - Column.copy(), as used in table.tometadata(), copies the 'doc' attribute. [ticket:2028] diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 9bb8f1a635..a3f70935c4 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -1392,8 +1392,15 @@ class RelationshipProperty(StrategizedProperty): return strategy.use_get def _refers_to_parent_table(self): + pt = self.parent.mapped_table + mt = self.mapper.mapped_table for c, f in self.synchronize_pairs: - if c.table is f.table: + if ( + pt.is_derived_from(c.table) and \ + pt.is_derived_from(f.table) and \ + mt.is_derived_from(c.table) and \ + mt.is_derived_from(f.table) + ): return True else: return False diff --git a/test/orm/inheritance/test_polymorph2.py b/test/orm/inheritance/test_polymorph2.py index abfbf6243f..2f12493797 100644 --- a/test/orm/inheritance/test_polymorph2.py +++ b/test/orm/inheritance/test_polymorph2.py @@ -12,6 +12,7 @@ from sqlalchemy.util import function_named from test.orm import _base, _fixtures from sqlalchemy.test.testing import eq_ from sqlalchemy.test.schema import Table, Column +from sqlalchemy.orm.interfaces import MANYTOONE class AttrSettable(object): def __init__(self, **kwargs): @@ -1121,3 +1122,116 @@ class MissingPolymorphicOnTest(_base.MappedTest): sess.expunge_all() eq_(sess.query(A).all(), [C(cdata='c1', adata='a1'), D(cdata='c2', adata='a2', ddata='d2')]) +class JoinedInhAdjacencyTest(_base.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table('people', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('type', String(30)), + ) + Table('users', metadata, + Column('id', Integer, ForeignKey('people.id'), + primary_key=True), + Column('supervisor_id', Integer, ForeignKey('people.id')), + ) + Table('dudes', metadata, + Column('id', Integer, ForeignKey('users.id'), + primary_key=True), + ) + + @classmethod + def setup_classes(cls): + class Person(_base.ComparableEntity): + pass + + class User(Person): + pass + + class Dude(User): + pass + + @testing.resolve_artifact_names + def _roundtrip(self): + sess = Session() + u1 = User() + u2 = User() + u2.supervisor = u1 + sess.add_all([u1, u2]) + sess.commit() + + assert u2.supervisor is u1 + + @testing.resolve_artifact_names + def _dude_roundtrip(self): + sess = Session() + u1 = User() + d1 = Dude() + d1.supervisor = u1 + sess.add_all([u1, d1]) + sess.commit() + + assert d1.supervisor is u1 + + @testing.resolve_artifact_names + def test_joined_to_base(self): + mapper(Person, people, + polymorphic_on=people.c.type, + polymorphic_identity='person', + ) + mapper(User, users, inherits=Person, + polymorphic_identity='user', + inherit_condition=(users.c.id == people.c.id), + properties = { + 'supervisor': relationship(Person, + primaryjoin=users.c.supervisor_id==people.c.id, + ), + } + ) + + assert User.supervisor.property.direction is MANYTOONE + self._roundtrip() + + @testing.resolve_artifact_names + def test_joined_to_same_subclass(self): + mapper(Person, people, + polymorphic_on=people.c.type, + polymorphic_identity='person', + ) + mapper(User, users, inherits=Person, + polymorphic_identity='user', + inherit_condition=(users.c.id == people.c.id), + properties = { + 'supervisor': relationship(User, + primaryjoin=users.c.supervisor_id==people.c.id, + remote_side=people.c.id, + foreign_keys=[users.c.supervisor_id] + ), + } + ) + assert User.supervisor.property.direction is MANYTOONE + self._roundtrip() + + @testing.resolve_artifact_names + def test_joined_subclass_to_superclass(self): + mapper(Person, people, + polymorphic_on=people.c.type, + polymorphic_identity='person', + ) + mapper(User, users, inherits=Person, + polymorphic_identity='user', + inherit_condition=(users.c.id == people.c.id), + ) + mapper(Dude, dudes, inherits=User, + polymorphic_identity='dude', + inherit_condition=(dudes.c.id==users.c.id), + properties={ + 'supervisor': relationship(User, + primaryjoin=users.c.supervisor_id==people.c.id, + remote_side=people.c.id, + foreign_keys=[users.c.supervisor_id] + ), + } + ) + assert Dude.supervisor.property.direction is MANYTOONE + self._dude_roundtrip()