From 2e9e2460344db642e1a1c6429b5c6e1aaa642703 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 4 Dec 2005 19:41:57 +0000 Subject: [PATCH] added order_by to EagerLoader, LazyLoader removed "scope" parameter from Mapper until we need to revisit that idea --- lib/sqlalchemy/mapping/__init__.py | 4 ++-- lib/sqlalchemy/mapping/mapper.py | 15 ++++-------- lib/sqlalchemy/mapping/properties.py | 18 +++++++++++---- lib/sqlalchemy/util.py | 6 +++++ test/mapper.py | 34 ++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/lib/sqlalchemy/mapping/__init__.py b/lib/sqlalchemy/mapping/__init__.py index 51e859c510..ebc35c06b7 100644 --- a/lib/sqlalchemy/mapping/__init__.py +++ b/lib/sqlalchemy/mapping/__init__.py @@ -53,10 +53,10 @@ def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=Non def _relation_mapper(class_, table=None, secondary=None, primaryjoin=None, secondaryjoin=None, - foreignkey=None, uselist=None, private=False, live=False, association=None, lazy=True, selectalias=None, **kwargs): + foreignkey=None, uselist=None, private=False, live=False, association=None, lazy=True, selectalias=None, order_by=None, **kwargs): return _relation_loader(mapper(class_, table, **kwargs), secondary, primaryjoin, secondaryjoin, - foreignkey=foreignkey, uselist=uselist, private=private, live=live, association=association, lazy=lazy, selectalias=selectalias) + foreignkey=foreignkey, uselist=uselist, private=private, live=live, association=association, lazy=lazy, selectalias=selectalias, order_by=order_by) class assignmapper(object): """provides a property object that will instantiate a Mapper for a given class the first diff --git a/lib/sqlalchemy/mapping/mapper.py b/lib/sqlalchemy/mapping/mapper.py index 52712eff9c..57e7887e99 100644 --- a/lib/sqlalchemy/mapping/mapper.py +++ b/lib/sqlalchemy/mapping/mapper.py @@ -34,7 +34,6 @@ class Mapper(object): class_, table, primarytable = None, - scope = "thread", properties = None, primary_key = None, is_primary = False, @@ -47,7 +46,6 @@ class Mapper(object): 'class_':class_, 'table':table, 'primarytable':primarytable, - 'scope':scope, 'properties':properties or {}, 'primary_key':primary_key, 'is_primary':False, @@ -62,7 +60,6 @@ class Mapper(object): self.extension = extension self.hashkey = hashkey self.class_ = class_ - self.scope = scope self.is_primary = is_primary if not issubclass(class_, object): @@ -450,7 +447,6 @@ class Mapper(object): statement.use_labels = True return statement - def _identity_key(self, row): return objectstore.get_row_key(row, self.class_, self.primarytable, self.pks_by_table[self.table]) @@ -503,7 +499,6 @@ class Mapper(object): instance = imap[identitykey] isnew = False - # plugin point # call further mapper properties on the row, to pull further @@ -517,7 +512,6 @@ class Mapper(object): return instance - class MapperProperty(object): """an element attached to a Mapper that describes and assists in the loading and saving of an attribute on an object instance.""" @@ -590,21 +584,22 @@ class TableFinder(sql.ClauseVisitor): def hash_key(obj): if obj is None: return 'None' + elif isinstance(obj, list): + return repr([hash_key(o) for o in obj]) elif hasattr(obj, 'hash_key'): return obj.hash_key() else: return repr(obj) -def mapper_hash_key(class_, table, primarytable = None, properties = None, scope = "thread", **kwargs): +def mapper_hash_key(class_, table, primarytable = None, properties = None, **kwargs): if properties is None: properties = {} return ( - "Mapper(%s, %s, primarytable=%s, properties=%s, scope=%s)" % ( + "Mapper(%s, %s, primarytable=%s, properties=%s)" % ( repr(class_), hash_key(table), hash_key(primarytable), - repr(dict([(k, hash_key(p)) for k,p in properties.iteritems()])), - scope + repr(dict([(k, hash_key(p)) for k,p in properties.iteritems()])) ) ) diff --git a/lib/sqlalchemy/mapping/properties.py b/lib/sqlalchemy/mapping/properties.py index 00b34762d6..fdce0c0e88 100644 --- a/lib/sqlalchemy/mapping/properties.py +++ b/lib/sqlalchemy/mapping/properties.py @@ -62,7 +62,7 @@ class PropertyLoader(MapperProperty): """describes an object property that holds a single item or list of items that correspond to a related database table.""" - def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, live=False, isoption=False, association=None, selectalias=None, **kwargs): + def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, live=False, isoption=False, association=None, selectalias=None, order_by=None, **kwargs): self.uselist = uselist self.argument = argument self.secondary = secondary @@ -74,7 +74,8 @@ class PropertyLoader(MapperProperty): self.isoption = isoption self.association = association self.selectalias = selectalias - self._hash_key = "%s(%s, %s, %s, %s, %s, %s, %s)" % (self.__class__.__name__, hash_key(self.argument), hash_key(secondary), hash_key(primaryjoin), hash_key(secondaryjoin), hash_key(foreignkey), repr(uselist), repr(private)) + self.order_by=util.to_list(order_by) + self._hash_key = "%s(%s, %s, %s, %s, %s, %s, %s, %s)" % (self.__class__.__name__, hash_key(self.argument), hash_key(secondary), hash_key(primaryjoin), hash_key(secondaryjoin), hash_key(foreignkey), repr(uselist), repr(private), hash_key(self.order_by)) def _copy(self): return self.__class__(self.mapper, self.secondary, self.primaryjoin, self.secondaryjoin, self.foreignkey, self.uselist, self.private) @@ -466,7 +467,9 @@ class LazyLoader(PropertyLoader): allparams = False break if allparams: - if self.secondary is not None: + if self.order_by is not None: + order_by = self.order_by + elif self.secondary is not None: order_by = [self.secondary.rowid_column] else: order_by = [] @@ -577,11 +580,16 @@ class EagerLoader(PropertyLoader): if self.secondaryjoin is not None: statement._outerjoin = sql.outerjoin(towrap, self.secondary, self.primaryjoin).outerjoin(self.eagertarget, self.eagersecondary) - statement.order_by(self.secondary.rowid_column) + if self.order_by is None: + statement.order_by(self.secondary.rowid_column) else: statement._outerjoin = towrap.outerjoin(self.eagertarget, self.eagerprimary) - statement.order_by(self.eagertarget.rowid_column) + if self.order_by is None: + statement.order_by(self.eagertarget.rowid_column) + if self.order_by is not None: + statement.order_by(*[self.eagertarget.get_col_by_original(c) for c in self.order_by]) + statement.append_from(statement._outerjoin) statement.append_column(self.eagertarget) recursion_stack[self] = True diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index 842d9ab320..c5ac8b979a 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -18,6 +18,12 @@ __all__ = ['OrderedProperties', 'OrderedDict'] import thread, weakref, UserList,string +def to_list(x): + if x is None: + return None + if not isinstance(x, list) and not isinstance(x, tuple): + return [x] + class OrderedProperties(object): """an object that maintains the order in which attributes are set upon it. also provides an iterator and a very basic dictionary interface to those attributes. diff --git a/test/mapper.py b/test/mapper.py index 54284f0c0d..a0b34d1720 100644 --- a/test/mapper.py +++ b/test/mapper.py @@ -120,6 +120,23 @@ class LazyTest(MapperSuperTest): {'user_id' : 7, 'addresses' : (Address, [{'address_id' : 1}])}, ) + def testorderby(self): + m = mapper(Address, addresses) + + m = mapper(User, users, properties = dict( + addresses = relation(m, lazy = True, order_by=addresses.c.email_address), + )) + l = m.select() + dict(address_id = 1, user_id = 7, email_address = "jack@bean.com"), + dict(address_id = 2, user_id = 8, email_address = "ed@wood.com"), + dict(address_id = 3, user_id = 8, email_address = "ed@lala.com") + + self.assert_result(l, User, + {'user_id' : 7, 'addresses' : (Address, [{'email_address' : 'jack@bean.com'}])}, + {'user_id' : 8, 'addresses' : (Address, [{'email_address':'ed@lala.com'}, {'email_address':'ed@wood.com'}])}, + {'user_id' : 9, 'addresses' : (Address, [])} + ) + def testonetoone(self): m = mapper(User, users, properties = dict( address = relation(Address, addresses, lazy = True, uselist = False) @@ -204,6 +221,23 @@ class EagerTest(MapperSuperTest): {'user_id' : 9, 'addresses' : (Address, [])} ) + def testorderby(self): + m = mapper(Address, addresses) + + m = mapper(User, users, properties = dict( + addresses = relation(m, lazy = False, order_by=addresses.c.email_address), + )) + l = m.select() + dict(address_id = 1, user_id = 7, email_address = "jack@bean.com"), + dict(address_id = 2, user_id = 8, email_address = "ed@wood.com"), + dict(address_id = 3, user_id = 8, email_address = "ed@lala.com") + + self.assert_result(l, User, + {'user_id' : 7, 'addresses' : (Address, [{'email_address' : 'jack@bean.com'}])}, + {'user_id' : 8, 'addresses' : (Address, [{'email_address':'ed@lala.com'}, {'email_address':'ed@wood.com'}])}, + {'user_id' : 9, 'addresses' : (Address, [])} + ) + def testonetoone(self): m = mapper(User, users, properties = dict( address = relation(Address, addresses, lazy = False, uselist = False) -- 2.47.2