]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
ORM descriptors such as hybrid properties can now be referenced
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Jul 2013 22:02:20 +0000 (18:02 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Jul 2013 22:02:56 +0000 (18:02 -0400)
by name in a string argument used with ``order_by``,
``primaryjoin``, or similar in :func:`.relationship`,
in addition to column-bound attributes. [ticket:2761]

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/ext/declarative/clsregistry.py
test/ext/declarative/test_basic.py

index 48f57e8cdb4c2d400ca6b4841e5f8bcce6da902f..d25cb8d67680fe669047adfd9555ab4f01a0b8f0 100644 (file)
@@ -6,6 +6,15 @@
 .. changelog::
     :version: 0.8.2
 
+    .. change::
+        :tags: feature, orm, declarative
+        :tickets: 2761
+
+        ORM descriptors such as hybrid properties can now be referenced
+        by name in a string argument used with ``order_by``,
+        ``primaryjoin``, or similar in :func:`.relationship`,
+        in addition to column-bound attributes.
+
     .. change::
         :tags: feature, firebird
         :tickets: 2763
index 89975716de8abdd7903cbf9f064254bb5d23a0c2..ab2b5ad49024b05b5fcec55aabaeb92ece51c047 100644 (file)
@@ -12,7 +12,7 @@ This system allows specification of classes and expressions used in
 from ...orm.properties import ColumnProperty, RelationshipProperty, \
                             SynonymProperty
 from ...schema import _get_table_key
-from ...orm import class_mapper
+from ...orm import class_mapper, interfaces
 from ... import util
 from ... import exc
 import weakref
@@ -190,19 +190,21 @@ class _GetColumns(object):
     def __getattr__(self, key):
         mp = class_mapper(self.cls, configure=False)
         if mp:
-            if not mp.has_property(key):
+            if key not in mp.all_orm_descriptors:
                 raise exc.InvalidRequestError(
                             "Class %r does not have a mapped column named %r"
                             % (self.cls, key))
 
-            prop = mp.get_property(key)
-            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"
-                            " directly to a Column)." % key)
+            desc = mp.all_orm_descriptors[key]
+            if desc.extension_type is interfaces.NOT_EXTENSION:
+                prop = desc.property
+                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"
+                                " directly to a Column)." % key)
         return getattr(self.cls, key)
 
 
index 0fe54a1549700430f0e34c21eb362433e6b1a928..4b5736c9f2a7030f0eaa9115ec14d58adba42961 100644 (file)
@@ -21,7 +21,10 @@ from sqlalchemy.testing.util import gc_collect
 
 Base = None
 
-class DeclarativeTestBase(fixtures.TestBase, testing.AssertsExecutionResults):
+class DeclarativeTestBase(fixtures.TestBase,
+                            testing.AssertsExecutionResults,
+                            testing.AssertsCompiledSQL):
+    __dialect__ = 'default'
     def setup(self):
         global Base
         Base = decl.declarative_base(testing.db)
@@ -290,7 +293,39 @@ class DeclarativeTest(DeclarativeTestBase):
         foo.rel = u1
         assert foo.rel == u1
 
-    def test_string_dependency_resolution_two(self):
+    def test_string_dependency_resolution_orm_descriptor(self):
+        from sqlalchemy.ext.hybrid import hybrid_property
+
+        class User(Base):
+            __tablename__ = 'user'
+            id = Column(Integer, primary_key=True)
+            firstname = Column(String(50))
+            lastname = Column(String(50))
+            game_id = Column(Integer, ForeignKey('game.id'))
+
+            @hybrid_property
+            def fullname(self):
+                return self.firstname + " " + self.lastname
+
+        class Game(Base):
+            __tablename__ = 'game'
+            id = Column(Integer, primary_key=True)
+            name = Column(String(50))
+            users = relationship("User", order_by="User.fullname")
+
+        s = Session()
+        self.assert_compile(
+            s.query(Game).options(joinedload(Game.users)),
+            "SELECT game.id AS game_id, game.name AS game_name, "
+            "user_1.id AS user_1_id, user_1.firstname AS user_1_firstname, "
+            "user_1.lastname AS user_1_lastname, "
+            "user_1.game_id AS user_1_game_id "
+            "FROM game LEFT OUTER JOIN \"user\" AS user_1 ON game.id = "
+            "user_1.game_id ORDER BY "
+            "user_1.firstname || :firstname_1 || user_1.lastname"
+        )
+
+    def test_string_dependency_resolution_no_table(self):
 
         class User(Base, fixtures.ComparableEntity):
             __tablename__ = 'users'