From e98e878646c809d466ff8cfb256022798e574fb4 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 18 Jul 2006 17:09:08 +0000 Subject: [PATCH] added "synonym()" function, applied to properties to have a propname the same as another, for the purposes of overriding props and allowing the original propname to be accessible in select_by(). --- CHANGES | 3 +++ doc/build/content/adv_datamapping.txt | 17 +++++++++++++++-- lib/sqlalchemy/orm/__init__.py | 5 ++++- lib/sqlalchemy/orm/mapper.py | 13 ++++++++++++- lib/sqlalchemy/orm/query.py | 14 ++++++++------ 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 326a8f3c34..9f3250224b 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,9 @@ any relationships. a flush() under some circumstances, this was fixed - expunge() was not working with cascade, fixed. - potential endless loop in cascading operations fixed. +- added "synonym()" function, applied to properties to have a +propname the same as another, for the purposes of overriding props +and allowing the original propname to be accessible in select_by(). 0.2.5 - fixed endless loop bug in select_by(), if the traversal hit diff --git a/doc/build/content/adv_datamapping.txt b/doc/build/content/adv_datamapping.txt index 98d79eab54..5c43a5b2c2 100644 --- a/doc/build/content/adv_datamapping.txt +++ b/doc/build/content/adv_datamapping.txt @@ -84,9 +84,22 @@ A common request is the ability to create custom class properties that override '_email': mytable.c.email }) -In a later release, SQLAlchemy will also allow `_get_email` and `_set_email` to be attached directly to the "email" property created by the mapper, and -will also allow this association to occur via decorators. +It is also possible to use the `select_by` and `get_by` functions on `Query` using the original property name, by establishing a `synonym`: + {python} + mapper(MyClass, mytable, proeprties = { + # map the '_email' attribute to the "email" column + # on the table + '_email': mytable.c.email + + # make a synonym 'email' + 'email' : synonym('_email') + }) + + # now you can select_by(email) + result = session.query(MyClass).select_by(email='john@smith.com') + +The `synonym` keyword is currently an [Alpha Feature][alpha_api]. #### Custom List Classes {@name=customlist} diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index 8b7d3df0b7..276510b18c 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -18,7 +18,7 @@ from session import Session as create_session __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query', - 'cascade_mappers', 'polymorphic_union', 'create_session', 'EXT_PASS' + 'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'EXT_PASS' ] def relation(*args, **kwargs): @@ -48,6 +48,9 @@ def mapper(class_, table=None, *args, **params): """returns a newMapper object.""" return Mapper(class_, table, *args, **params) +def synonym(name): + return SynonymProperty(name) + def clear_mappers(): """removes all mappers that have been created thus far. when new mappers are created, they will be assigned to their classes as their primary mapper.""" diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 3fb5bc000c..de126f3d9c 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -12,7 +12,7 @@ import query as querylib import session as sessionlib import weakref -__all__ = ['Mapper', 'MapperExtension', 'class_mapper', 'object_mapper', 'EXT_PASS'] +__all__ = ['Mapper', 'MapperExtension', 'class_mapper', 'object_mapper', 'SynonymProperty', 'EXT_PASS'] # a dictionary mapping classes to their primary mappers mapper_registry = weakref.WeakKeyDictionary() @@ -1129,6 +1129,17 @@ class MapperProperty(object): at the class level. otherwise we have to set attribute behavior on a per-instance level.""" return self.parent._is_primary_mapper() +class SynonymProperty(MapperProperty): + """a marker object used by query.select_by to allow a property name to refer to another. + + this object may be expanded in the future.""" + def __init__(self, name): + self.name = name + def execute(self, session, instance, row, identitykey, imap, isnew): + pass + def copy(self): + return SynonymProperty(self.name) + class MapperOption(object): """describes a modification to a Mapper in the context of making a copy of it. This is used to assist in the prototype pattern used by mapper.options().""" diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 2b257e7169..5006b8ad4d 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -116,17 +116,19 @@ class Query(object): import properties keys = [] seen = util.Set() - def search_for_prop(mapper): - if mapper in seen: + def search_for_prop(mapper_): + if mapper_ in seen: return None - seen.add(mapper) - if mapper.props.has_key(key): - prop = mapper.props[key] + seen.add(mapper_) + if mapper_.props.has_key(key): + prop = mapper_.props[key] + if isinstance(prop, mapper.SynonymProperty): + prop = mapper_.props[prop.name] if isinstance(prop, properties.PropertyLoader): keys.insert(0, prop.key) return prop else: - for prop in mapper.props.values(): + for prop in mapper_.props.values(): if not isinstance(prop, properties.PropertyLoader): continue x = search_for_prop(prop.mapper) -- 2.47.2