subclass table to the query separately producing a
cartesian product. An example is in the ticket
description. [ticket:1543]
+
+ - query.options() now only propagate to loaded objects
+ for potential further sub-loads only for options where
+ such behavior is relevant, keeping
+ various unserializable options like those generated
+ by contains_eager() out of individual instance states.
+ [ticket:1553]
0.5.6
=====
if kwargs:
raise exceptions.ArgumentError("Invalid kwargs for contains_eager: %r" % kwargs.keys())
- return (strategies.EagerLazyOption(keys, lazy=False, _only_on_lead=True), strategies.LoadEagerFromAliasOption(keys, alias=alias))
+ return (strategies.EagerLazyOption(keys, lazy=False, propagate_to_loaders=False), strategies.LoadEagerFromAliasOption(keys, alias=alias))
@sa_util.accepts_a_list_as_starargs(list_deprecation='pending')
def defer(*keys):
class MapperOption(object):
"""Describe a modification to a Query."""
+ propagate_to_loaders = False
+ """if True, indicate this option should be carried along
+ Query object generated by scalar or object lazy loaders.
+ """
+
def process_query(self, query):
pass
class ExtensionOption(MapperOption):
"""a MapperOption that applies a MapperExtension to a query operation."""
-
+
def __init__(self, ext):
self.ext = ext
def populate_state(state, dict_, row, isnew, only_load_props, **flags):
if isnew:
- if context.options:
- state.load_options = context.options
- if state.load_options:
- state.load_path = context.query._current_path + path
-
- if isnew:
- if context.options:
- state.load_options = context.options
+ if context.propagate_options:
+ state.load_options = context.propagate_options
if state.load_options:
state.load_path = context.query._current_path + path
self.adapter = None
self.options = set(query._with_options)
+ self.propagate_options = self.options.difference(o for o in self.options if not o.propagate_to_loaders)
self.attributes = query._attributes.copy()
class AliasOption(interfaces.MapperOption):
return attributes.ATTR_WAS_SET
class DeferredOption(StrategizedOption):
+ propagate_to_loaders = True
+
def __init__(self, key, defer=False):
super(DeferredOption, self).__init__(key)
self.defer = defer
return ColumnLoader
class UndeferGroupOption(MapperOption):
+ propagate_to_loaders = True
+
def __init__(self, group):
self.group = group
def process_query(self, query):
log.class_logger(EagerLoader)
class EagerLazyOption(StrategizedOption):
- def __init__(self, key, lazy=True, chained=False, mapper=None, _only_on_lead=False):
+
+ def __init__(self, key, lazy=True, chained=False, mapper=None, propagate_to_loaders=True):
super(EagerLazyOption, self).__init__(key, mapper)
self.lazy = lazy
self.chained = chained
- self._only_on_lead = _only_on_lead
+ self.propagate_to_loaders = propagate_to_loaders
- def process_query_conditionally(self, query):
- if not self._only_on_lead:
- StrategizedOption.process_query_conditionally(self, query)
-
def is_chained(self):
return not self.lazy and self.chained
return NoLoader
class LoadEagerFromAliasOption(PropertyOption):
+
def __init__(self, key, alias=None):
super(LoadEagerFromAliasOption, self).__init__(key)
if alias is not None:
m, alias, is_aliased_class = mapperutil._entity_info(alias)
self.alias = alias
- def process_query_conditionally(self, query):
- # dont run this option on a secondary load
- pass
-
def process_query_property(self, query, paths, mappers):
if self.alias is not None:
if isinstance(self.alias, basestring):
eq_(l, self.static.user_address_result)
self.sql_count_(4, go)
-
@testing.resolve_artifact_names
def test_eager_degrade_deep(self):
# test with a deeper set of eager loads. when we first load the three
eq_(l, self.static.user_address_result)
self.sql_count_(4, go)
+ @testing.resolve_artifact_names
+ def test_option_propagate(self):
+ mapper(User, users, properties=dict(
+ orders = relation(Order)
+ ))
+ mapper(Order, orders, properties=dict(
+ items = relation(Item, secondary=order_items)
+ ))
+ mapper(Item, items)
+
+ sess = create_session()
+
+ oalias = aliased(Order)
+ opt1 = sa.orm.eagerload(User.orders, Order.items)
+ opt2a, opt2b = sa.orm.contains_eager(User.orders, Order.items, alias=oalias)
+ u1 = sess.query(User).join((oalias, User.orders)).options(opt1, opt2a, opt2b).first()
+ ustate = attributes.instance_state(u1)
+ assert opt1 in ustate.load_options
+ assert opt2a not in ustate.load_options
+ assert opt2b not in ustate.load_options
+
+ import pickle
+ pickle.dumps(u1)
class DeepOptionsTest(_fixtures.FixtureTest):
@classmethod