Commits

Anonymous committed a730364

Allow use of synonyms in primaryjoin / secondaryjoin conditions

  • Participants
  • Parent commits 79b0b8c

Comments (0)

Files changed (4)

doc/build/changelog/changelog_08.rst

     :version: 0.8.0b2
 
     .. change::
+        :tags: orm, feature
+
+      Allow synonyms to be used when defining primary and secondary
+      joins for relationships.
+
+    .. change::
         :tags: orm, bug
         :ticket: 2614
 
         :tags: sql, removed
 
         The long-deprecated and non-functional ``assert_unicode`` flag on
-        :func:`.create_engine` as well as :class:`.String` is removed.
+        :func:`.create_engine` as well as :class:`.String` is removed.

lib/sqlalchemy/ext/declarative/clsregistry.py

 :func:`.relationship` using strings.
 
 """
-from ...orm.properties import ColumnProperty, RelationshipProperty
+from ...orm.properties import ColumnProperty, RelationshipProperty, \
+                            SynonymProperty
 from ...schema import _get_table_key
 from ...orm import class_mapper
 from ... import util
                             % (self.cls, key))
 
             prop = mp.get_property(key)
-            if not isinstance(prop, ColumnProperty):
+            if isinstance(prop, SynonymProperty):
+                key = prop.name
+            elif not isinstance(prop, ColumnProperty):
                 raise exc.InvalidRequestError(
                             "Property %r is not an instance of"
                             " ColumnProperty (i.e. does not correspond"

test/ext/declarative/test_basic.py

     Session
 from sqlalchemy.testing import eq_
 from sqlalchemy.util import classproperty
-from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase, ConcreteBase
+from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase, \
+    ConcreteBase, synonym_for
 from sqlalchemy.testing import fixtures
 from sqlalchemy.testing.util import gc_collect
 
                               "'addresses' is not an instance of "
                               "ColumnProperty", configure_mappers)
 
+    def test_string_dependency_resolution_synonym(self):
+        from sqlalchemy.sql import desc
+
+        class User(Base, fixtures.ComparableEntity):
+
+            __tablename__ = 'users'
+            id = Column(Integer, primary_key=True,
+                        test_needs_autoincrement=True)
+            name = Column(String(50))
+
+        Base.metadata.create_all()
+        sess = create_session()
+        u1 = User(name='ed')
+        sess.add(u1)
+        sess.flush()
+        sess.expunge_all()
+        eq_(sess.query(User).filter(User.name == 'ed').one(),
+            User(name='ed'))
+
+        class Foo(Base, fixtures.ComparableEntity):
+
+            __tablename__ = 'foo'
+            id = Column(Integer, primary_key=True)
+            _user_id = Column(Integer) 
+            rel = relationship('User',
+                               uselist=False,
+                               foreign_keys=[User.id],
+                               primaryjoin='Foo.user_id==User.id')
+
+            @synonym_for('_user_id')
+            @property
+            def user_id(self):
+                return self._user_id
+
+        foo = Foo()
+        foo.rel = u1
+        assert foo.rel == u1
+
     def test_string_dependency_resolution_two(self):
 
         class User(Base, fixtures.ComparableEntity):

test/orm/test_relationships.py

                     backref, create_session, configure_mappers, \
                     clear_mappers, sessionmaker, attributes,\
                     Session, composite, column_property, foreign,\
-                    remote
+                    remote, synonym
 from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE, MANYTOMANY
 from sqlalchemy.testing import eq_, startswith_, AssertsCompiledSQL, is_
 from sqlalchemy.testing import fixtures
         )
 
 
+class SynonymsAsFKsTest(fixtures.MappedTest):
+    """Syncrules on foreign keys that are also primary"""
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table("tableA", metadata,
+              Column("id",Integer,primary_key=True,
+                            test_needs_autoincrement=True),
+              Column("foo",Integer,),
+              test_needs_fk=True)
+
+        Table("tableB",metadata,
+              Column("id",Integer,primary_key=True,
+                            test_needs_autoincrement=True),
+              Column("_a_id", Integer, key='a_id', primary_key=True),
+              test_needs_fk=True)
+
+    @classmethod
+    def setup_classes(cls):
+        class A(cls.Basic):
+            pass
+
+        class B(cls.Basic):
+            @property
+            def a_id(self):
+                return self._a_id
+
+    def test_synonym_fk(self):
+        """test that active history is enabled on a
+        one-to-many/one that has use_get==True"""
+
+        tableB, A, B, tableA = (self.tables.tableB,
+                                self.classes.A,
+                                self.classes.B,
+                                self.tables.tableA)
+
+        mapper(B, tableB, properties={
+            'a_id': synonym('_a_id', map_column=True)})
+        mapper(A, tableA, properties={
+            'b': relationship(B, primaryjoin=(tableA.c.id == foreign(B.a_id)),
+                              uselist=False)})
+
+        sess = create_session()
+
+        b = B(id=0)
+        a = A(id=0, b=b)
+        sess.add(a)
+        sess.add(b)
+        sess.flush()
+        sess.expunge_all()
+
+        assert a.b == b
+        assert a.id == b.a_id
+        assert a.id == b._a_id
+
+
 class FKsAsPksTest(fixtures.MappedTest):
     """Syncrules on foreign keys that are also primary"""