import properties, strategies
from session import Session as create_session
-__all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer',
+__all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'extension',
'mapper', 'clear_mappers', 'clear_mapper', 'sql', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query',
'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_eager', 'EXT_PASS'
]
new primary mapper."""
del mapper_registry[m.class_key]
+def extension(ext):
+ """return a MapperOption that will insert the given MapperExtension to the
+ beginning of the list of extensions that will be called in the context of the Query.
+
+ used with query.options()."""
+ return mapperlib.ExtensionOption(ext)
+
def eagerload(name):
"""return a MapperOption that will convert the property of the given name
into an eager load.
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-from sqlalchemy import util
+from sqlalchemy import util, logging
class MapperProperty(object):
"""manages the relationship of a Mapper to a single class attribute, as well
pass
def process_selection_context(self, context):
pass
-
+ def process_query(self, query):
+ pass
+
class PropertyOption(MapperOption):
"""a MapperOption that is applied to a property off the mapper
or one of its child mappers, identified by a dot-separated key."""
mapper = getattr(prop, 'mapper', None)
self.__prop = prop
return prop
+PropertyOption.logger = logging.class_logger(PropertyOption)
class StrategizedOption(PropertyOption):
"""a MapperOption that affects which LoaderStrategy will be used for an operation
by a StrategizedProperty."""
def process_query_property(self, context, property):
+ self.logger.debug("applying option to QueryContext, property key '%s'" % self.key)
context.attributes[(LoaderStrategy, property)] = self.get_strategy_class()
def process_selection_property(self, context, property):
+ self.logger.debug("applying option to SelectionContext, property key '%s'" % self.key)
context.attributes[(LoaderStrategy, property)] = self.get_strategy_class()
def get_strategy_class(self):
raise NotImplementedError()
class _ExtensionCarrier(MapperExtension):
def __init__(self):
self.elements = []
+ def insert(self, extension):
+ """insert a MapperExtension at the beginning of this ExtensionCarrier's list."""
+ self.elements.insert(0, extension)
+ def append(self, extension):
+ """append a MapperExtension at the end of this ExtensionCarrier's list."""
+ self.elements.append(extension)
# TODO: shrink down this approach using __getattribute__ or similar
def get_session(self):
return self._do('get_session')
def _do(self, funcname, *args, **kwargs):
for elem in self.elements:
if elem is self:
- raise "WTF"
+ raise exceptions.AssertionError("ExtensionCarrier set to itself")
ret = getattr(elem, funcname)(*args, **kwargs)
if ret is not EXT_PASS:
return ret
else:
return EXT_PASS
-
+class ExtensionOption(MapperExtension):
+ def __init__(self, ext):
+ self.ext = ext
+ def process_query(self, query):
+ query._insert_extension(self.ext)
+
class ClassKey(object):
"""keys a class and an entity name to a mapper, via the mapper_registry."""
__metaclass__ = util.ArgSingleton
class Query(object):
"""encapsulates the object-fetching operations provided by Mappers."""
- def __init__(self, class_or_mapper, session=None, entity_name=None, lockmode=None, with_options=None, **kwargs):
+ def __init__(self, class_or_mapper, session=None, entity_name=None, lockmode=None, with_options=None, extension=None, **kwargs):
if isinstance(class_or_mapper, type):
self.mapper = mapper.class_mapper(class_or_mapper, entity_name=entity_name)
else:
self.always_refresh = kwargs.pop('always_refresh', self.mapper.always_refresh)
self.order_by = kwargs.pop('order_by', self.mapper.order_by)
self.lockmode = lockmode
- self.extension = kwargs.pop('extension', self.mapper.extension)
+ self.extension = mapper._ExtensionCarrier()
+ if extension is not None:
+ self.extension.append(extension)
+ self.extension.append(self.mapper.extension)
self._session = session
if not hasattr(self.mapper, '_get_clause'):
_get_clause = sql.and_()
_get_clause.clauses.append(primary_key == sql.bindparam(primary_key._label, type=primary_key.type))
self.mapper._get_clause = _get_clause
self._get_clause = self.mapper._get_clause
+ for opt in self.with_options:
+ opt.process_query(self)
+
+ def _insert_extension(self, ext):
+ self.extension.insert(ext)
+
def _get_session(self):
if self._session is None:
return self.mapper.get_session()
return EagerLoader
elif self.lazy is None:
return NoLoader
+EagerLazyOption.logger = logging.class_logger(EagerLazyOption)
class RowDecorateOption(PropertyOption):
def __init__(self, key, decorator=None):
self.decorator = decorator
def process_selection_property(self, context, property):
context.attributes[(EagerLoader, property)] = self.decorator
+RowDecorateOption.logger = logging.class_logger(RowDecorateOption)
u = sess.query(User).get_by(uname='jack')
self.assert_result(u.adlist, Address, *(user_address_result[0]['addresses'][1]))
+
+ def testextensionoptions(self):
+ sess = create_session()
+ mapper(User, users)
+ class testext(MapperExtension):
+ def select_by(self, *args, **kwargs):
+ return "HI"
+ l = sess.query(User).options(extension(testext())).select_by(x=5)
+ assert l == "HI"
def testeageroptions(self):
"""tests that a lazy relation can be upgraded to an eager relation via the options method"""