]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- replaced calls for mapper.props in Query with mapper.get_property(),
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 5 Jul 2007 00:09:06 +0000 (00:09 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 5 Jul 2007 00:09:06 +0000 (00:09 +0000)
which resolves synonyms.  fixes [ticket:598] for join/join_to/join_via/with_parent

CHANGES
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/query.py
test/orm/mapper.py

diff --git a/CHANGES b/CHANGES
index 37f24deb2fc09c354cb5bd1acd5fe96678dbb436..a20bc4cc8b1cb9eeb9b78c75d4d6aa625d2e23b0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -14,6 +14,8 @@
     - added synchronization to the mapper() construction step, to avoid
       thread collections when pre-existing mappers are compiling in a 
       different thread [ticket:613]
+    - synonym() properties are fully supported by all Query joining/
+      with_parent operations [ticket:598]
     - fixed very stupid bug when deleting items with many-to-many
       uselist=False relations
     - remember all that stuff about polymorphic_union ?  for 
index b037d0d1846f75b53d16fe24e54486434962d82b..37efdeb1fc57e10819f9f59cfbbd125e3b8203ac 100644 (file)
@@ -8,7 +8,7 @@ from sqlalchemy import sql, schema, util, exceptions, logging
 from sqlalchemy import sql_util as sqlutil
 from sqlalchemy.orm import util as mapperutil
 from sqlalchemy.orm import sync
-from sqlalchemy.orm.interfaces import MapperProperty, MapperOption, OperationContext
+from sqlalchemy.orm.interfaces import MapperProperty, MapperOption, OperationContext, SynonymProperty
 import weakref
 
 __all__ = ['Mapper', 'MapperExtension', 'class_mapper', 'object_mapper', 'EXT_PASS', 'mapper_registry', 'ExtensionOption']
@@ -302,7 +302,26 @@ class Mapper(object):
         return self.__props
 
     props = property(_get_props, doc="compiles this mapper if needed, and returns the "
-                     "dictionary of MapperProperty objects associated with this mapper.")
+                     "dictionary of MapperProperty objects associated with this mapper."
+                     "(Deprecated; use get_property() and iterate_properties)")
+
+    def get_property(self, key, resolve_synonyms=False, raiseerr=True):
+        """return MapperProperty with the given key.
+
+        forwards compatible with 0.4.
+        """
+
+        self.compile()
+        prop = self.__props.get(key, None)
+        if resolve_synonyms:
+            while isinstance(prop, SynonymProperty):
+                prop = self.__props.get(prop.name, None)
+        if prop is None and raiseerr:
+            raise exceptions.InvalidRequestError("Mapper '%s' has no property '%s'" % (str(self), key))
+        return prop
+
+    iterate_properties = property(lambda self: self._get_props().itervalues(), doc="returns an iterator of all MapperProperty objects."
+                                    "  Forwards compatible with 0.4")
 
     def compile(self):
         """Compile this mapper into its final internal format.
index 2ad704702791269969228ad24feb4cee1020f627..9c3feacf05dada7246c1a31b89dd457bd2535ff1 100644 (file)
@@ -208,7 +208,7 @@ class Query(object):
         mapper = self.mapper
         clause = None
         for key in keys:
-            prop = mapper.props[key]
+            prop = mapper.get_property(key, resolve_synonyms=True)
             if clause is None:
                 clause = prop.get_join(mapper)
             else:
@@ -403,7 +403,7 @@ class Query(object):
         """
         
         mapper = object_mapper(instance)
-        prop = mapper.props[property]
+        prop = mapper.get_property(property, resolve_synonyms=True)
         target = prop.mapper
         criterion = cls._with_lazy_criterion(instance, prop)
         return Query(target, **kwargs).filter(criterion)
@@ -427,13 +427,13 @@ class Query(object):
         from sqlalchemy.orm import properties
         mapper = object_mapper(instance)
         if property is None:
-            for prop in mapper.props.values():
+            for prop in mapper.iterate_properties:
                 if isinstance(prop, properties.PropertyLoader) and prop.mapper is self.mapper:
                     break
             else:
                 raise exceptions.InvalidRequestError("Could not locate a property which relates instances of class '%s' to instances of class '%s'" % (self.mapper.class_.__name__, instance.__class__.__name__))
         else:
-            prop = mapper.props[property]
+            prop = mapper.get_property(property, resolve_synonyms=True)
         return self.filter(Query._with_lazy_criterion(instance, prop))
 
     def add_entity(self, entity):
@@ -524,20 +524,13 @@ class Query(object):
 
     def _join_to(self, prop, outerjoin=False):
         if isinstance(prop, list):
-            mapper = self._joinpoint
-            keys = []
-            for key in prop:
-                p = mapper.props[key]
-                if p._is_self_referential():
-                    raise exceptions.InvalidRequestError("Self-referential query on '%s' property must be constructed manually using an Alias object for the related table." % (str(p)))
-                keys.append(key)
-                mapper = p.mapper
+            keys = prop
         else:
             [keys,p] = self._locate_prop(prop, start=self._joinpoint)
         clause = self._from_obj[-1]
         mapper = self._joinpoint
         for key in keys:
-            prop = mapper.props[key]
+            prop = mapper.get_property(key, resolve_synonyms=True)
             if prop._is_self_referential():
                 raise exceptions.InvalidRequestError("Self-referential query on '%s' property must be constructed manually using an Alias object for the related table." % str(prop))
             if outerjoin:
@@ -593,14 +586,12 @@ class Query(object):
                 return None
             seen.add(mapper_)
             if mapper_.props.has_key(key):
-                prop = mapper_.props[key]
-                if isinstance(prop, SynonymProperty):
-                    prop = mapper_.props[prop.name]
+                prop = mapper_.get_property(key, resolve_synonyms=True)
                 if isinstance(prop, properties.PropertyLoader):
                     keys.insert(0, prop.key)
                 return prop
             else:
-                for prop in mapper_.props.values():
+                for prop in mapper_.iterate_properties:
                     if not isinstance(prop, properties.PropertyLoader):
                         continue
                     x = search_for_prop(prop.mapper)
@@ -1103,7 +1094,7 @@ class Query(object):
         # give all the attached properties a chance to modify the query
         # TODO: doing this off the select_mapper.  if its the polymorphic mapper, then
         # it has no relations() on it.  should we compile those too into the query ?  (i.e. eagerloads)
-        for value in self.select_mapper.props.values():
+        for value in self.select_mapper.iterate_properties:
             value.setup(context)
         
         # additional entities/columns, add those to selection criterion
@@ -1111,7 +1102,7 @@ class Query(object):
             if isinstance(m, type):
                 m = mapper.class_mapper(m)
             if isinstance(m, mapper.Mapper):
-                for value in m.props.values():
+                for value in m.iterate_properties:
                     value.setup(context)
             elif isinstance(m, sql.ColumnElement):
                 statement.append_column(m)
index f3d94c10f5aaf969cd7067de9b9bedf28daecc9a..88b474e24263b4f141e5fade376fd83ed631872e 100644 (file)
@@ -304,9 +304,12 @@ class MapperTest(MapperSuperTest):
         """test the with_parent()) method and one-to-many relationships"""
         
         m = mapper(User, users, properties={
+            'user_name_syn':synonym('user_name'),
             'orders':relation(mapper(Order, orders, properties={
-                'items':relation(mapper(Item, orderitems))
-            }))
+                'items':relation(mapper(Item, orderitems)),
+                'items_syn':synonym('items')
+            })),
+            'orders_syn':synonym('orders')
         })
 
         sess = create_session()
@@ -334,6 +337,21 @@ class MapperTest(MapperSuperTest):
             assert False
         except exceptions.InvalidRequestError, e:
             assert str(e) == "Could not locate a property which relates instances of class 'Item' to instances of class 'User'"
+
+
+        for nameprop, orderprop in (
+            ('user_name', 'orders'),
+            ('user_name_syn', 'orders'),
+            ('user_name', 'orders_syn'),
+            ('user_name_syn', 'orders_syn'),
+        ):
+            sess = create_session()
+            q = sess.query(User)
+
+            u1 = q.filter_by(**{nameprop:'jack'}).one()
+
+            o = sess.query(Order).with_parent(u1, property=orderprop).list()
+            self.assert_result(o, Order, *user_all_result[0]['orders'][1])
             
     def testwithparentm2m(self):
         """test the with_parent() method and many-to-many relationships"""
@@ -347,21 +365,36 @@ class MapperTest(MapperSuperTest):
         self.assert_result(k, Keyword, *item_keyword_result[1]['keywords'][1])
 
         
-    def testautojoin(self):
+    def test_join(self):
         """test functions derived from Query's _join_to function."""
         
         m = mapper(User, users, properties={
             'orders':relation(mapper(Order, orders, properties={
-                'items':relation(mapper(Item, orderitems))
-            }))
+                'items':relation(mapper(Item, orderitems)),
+                'items_syn':synonym('items')
+            })),
+            
+            'orders_syn':synonym('orders'),
         })
 
         sess = create_session()
         q = sess.query(m)
 
-        l = q.filter(orderitems.c.item_name=='item 4').join(['orders', 'items']).list()
-        self.assert_result(l, User, user_result[0])
-        
+        for j in (
+            ['orders', 'items'],
+            ['orders', 'items_syn'],
+            ['orders_syn', 'items'],
+            ['orders_syn', 'items_syn'],
+        ):
+            for q in (
+                q.filter(orderitems.c.item_name=='item 4').join(j),
+                q.filter(orderitems.c.item_name=='item 4').join(j[-1]),
+                q.filter(orderitems.c.item_name=='item 4').filter(q.join_via(j)),
+                q.filter(orderitems.c.item_name=='item 4').filter(q.join_to(j[-1])),
+            ):
+                l = q.all()
+                self.assert_result(l, User, user_result[0])
+
         l = q.select_by(item_name='item 4')
         self.assert_result(l, User, user_result[0])
 
@@ -1269,9 +1302,6 @@ class EagerTest(MapperSuperTest):
             l = q.select(q.join_to('orders'), order_by=desc(orders.c.user_id), limit=2, offset=1)
             self.assert_result(l, User, *(user_all_result[2], user_all_result[0]))
         
-        l = q.select(q.join_to('addresses'), order_by=desc(addresses.c.email_address), limit=1, offset=0)
-        self.assert_result(l, User, *(user_all_result[0],))
-        
     def testonetoone(self):
         m = mapper(User, users, properties = dict(
             address = relation(mapper(Address, addresses), lazy = False, uselist = False)