is constructed with individual calls to append_column(); this fixes an ORM
bug whereby nested select statements were not getting correlated with the
main select generated by the Query object.
+- speed enhancements to ORM object instantiation, eager loading of rows
0.3.3
- string-based FROM clauses fixed, i.e. select(..., from_obj=["sometext"])
#print self, "register attribute", key, "for class", class_
if not hasattr(class_, '_state'):
def _get_state(self):
- try:
- return self.__sa_attr_state
- except AttributeError:
- self.__sa_attr_state = {}
- return self.__sa_attr_state
+ if not hasattr(self, '_sa_attr_state'):
+ self._sa_attr_state = {}
+ return self._sa_attr_state
class_._state = property(_get_state)
typecallable = kwargs.pop('typecallable', None)
There is a single default strategy selected, and alternate strategies can be selected
at selection time through the usage of StrategizedOption objects."""
def _get_context_strategy(self, context):
- return self._get_strategy(context.attributes.get((LoaderStrategy, self), self.strategy.__class__))
+ try:
+ return context.attributes[id(self)]
+ except KeyError:
+ # cache the located strategy per StrategizedProperty in the given context for faster re-lookup
+ ctx_strategy = self._get_strategy(context.attributes.get((LoaderStrategy, self), self.strategy.__class__))
+ context.attributes[id(self)] = ctx_strategy
+ return ctx_strategy
def _get_strategy(self, cls):
try:
return self._all_strategies[cls]
except KeyError:
+ # cache the located strategy per class for faster re-lookup
strategy = cls(self)
strategy.init()
strategy.is_default = False
class _ExtensionCarrier(MapperExtension):
def __init__(self):
self.__elements = []
- self.__callables = {}
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)
- def __getattribute__(self, key):
- if key in MapperExtension.__dict__:
- try:
- return self.__callables[key]
- except KeyError:
- return self.__callables.setdefault(key, lambda *args, **kwargs:self._do(key, *args, **kwargs))
- else:
- return super(_ExtensionCarrier, self).__getattribute__(key)
+ def get_session(self, *args, **kwargs):
+ return self._do('get_session', *args, **kwargs)
+ def load(self, *args, **kwargs):
+ return self._do('load', *args, **kwargs)
+ def get(self, *args, **kwargs):
+ return self._do('get', *args, **kwargs)
+ def get_by(self, *args, **kwargs):
+ return self._do('get_by', *args, **kwargs)
+ def select_by(self, *args, **kwargs):
+ return self._do('select_by', *args, **kwargs)
+ def select(self, *args, **kwargs):
+ return self._do('select', *args, **kwargs)
+ def create_instance(self, *args, **kwargs):
+ return self._do('create_instance', *args, **kwargs)
+ def append_result(self, *args, **kwargs):
+ return self._do('append_result', *args, **kwargs)
+ def populate_instance(self, *args, **kwargs):
+ return self._do('populate_instance', *args, **kwargs)
+ def before_insert(self, *args, **kwargs):
+ return self._do('before_insert', *args, **kwargs)
+ def before_update(self, *args, **kwargs):
+ return self._do('before_update', *args, **kwargs)
+ def after_update(self, *args, **kwargs):
+ return self._do('after_update', *args, **kwargs)
+ def after_insert(self, *args, **kwargs):
+ return self._do('after_insert', *args, **kwargs)
+ def before_delete(self, *args, **kwargs):
+ return self._do('before_delete', *args, **kwargs)
+ def after_delete(self, *args, **kwargs):
+ return self._do('after_delete', *args, **kwargs)
def _do(self, funcname, *args, **kwargs):
for elem in self.__elements:
- if elem is self:
- raise exceptions.AssertionError("ExtensionCarrier set to itself")
ret = getattr(elem, funcname)(*args, **kwargs)
if ret is not EXT_PASS:
return ret
for value in self.mapper.props.values():
value.setup(context, eagertable=clauses.eagertarget, parentclauses=clauses, parentmapper=self.mapper)
+ def _create_row_processor(self, selectcontext, row):
+ """create a 'row processing' function that will apply eager aliasing to the row.
+
+ also check that an identity key can be retrieved from the row, else return None."""
+ # check for a user-defined decorator in the SelectContext (which was set up by the contains_eager() option)
+ if selectcontext.attributes.has_key((EagerLoader, self.parent_property)):
+ # custom row decoration function, placed in the selectcontext by the
+ # contains_eager() mapper option
+ decorator = selectcontext.attributes[(EagerLoader, self.parent_property)]
+ if decorator is None:
+ decorator = lambda row: row
+ else:
+ try:
+ # decorate the row according to the stored AliasedClauses for this eager load
+ clauses = self.clauses_by_lead_mapper[selectcontext.mapper]
+ decorator = clauses._row_decorator
+ except KeyError:
+ # no stored AliasedClauses: eager loading was not set up in the query and
+ # AliasedClauses never got initialized
+ return None
+
+ try:
+ decorated_row = decorator(row)
+ # check for identity key
+ identity_key = self.mapper.identity_key_from_row(decorated_row)
+ # and its good
+ return decorator
+ except KeyError:
+ # no identity key - dont return a row processor, will cause a degrade to lazy
+ return None
+
def process_row(self, selectcontext, instance, row, identitykey, isnew):
"""receive a row. tell our mapper to look for a new object instance in the row, and attach
it to a list on the parent instance."""
-
if self in selectcontext.recursion_stack:
return
try:
- # decorate the row according to the stored AliasedClauses for this eager load,
- # or look for a user-defined decorator in the SelectContext (which was set up by the contains_eager() option)
- if selectcontext.attributes.has_key((EagerLoader, self.parent_property)):
- # custom row decoration function, placed in the selectcontext by the
- # contains_eager() mapper option
- decorator = selectcontext.attributes[(EagerLoader, self.parent_property)]
- if decorator is None:
- decorated_row = row
- else:
- decorated_row = decorator(row)
- else:
- clauses = self.clauses_by_lead_mapper[selectcontext.mapper]
- decorated_row = clauses._decorate_row(row)
- # check for identity key
- identity_key = self.mapper.identity_key_from_row(decorated_row)
+ # check for row processor
+ row_processor = selectcontext.attributes[id(self)]
except KeyError:
- # else degrade to a lazy loader
+ # create a row processor function and cache it in the context
+ row_processor = self._create_row_processor(selectcontext, row)
+ selectcontext.attributes[id(self)] = row_processor
+
+ if row_processor is not None:
+ decorated_row = row_processor(row)
+ else:
+ # row_processor was None: degrade to a lazy loader
if self._should_log_debug:
self.logger.debug("degrade to lazy loader on %s" % mapperutil.attribute_str(instance, self.key))
self.parent_property._get_strategy(LazyLoader).process_row(selectcontext, instance, row, identitykey, isnew)