diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-08-19 12:35:39 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-08-19 12:35:39 -0400 |
| commit | 3087b8ddef6e903aff95e14742888cf2804a9206 (patch) | |
| tree | 37128086224656393b4f888b673b391df3532824 /lib | |
| parent | b47c185fc4b09f0ee8f8445fb1b7ea41beafa0d7 (diff) | |
| download | sqlalchemy-3087b8ddef6e903aff95e14742888cf2804a9206.tar.gz | |
- [bug] Lazy loads emitted within flush events
such as before_flush(), before_update(),
etc. will now function as they would
within non-event code, regarding consideration
of the PK/FK values used in the lazy-emitted
query. Previously,
special flags would be established that
would cause lazy loads to load related items
based on the "previous" value of the
parent PK/FK values specifically when called
upon within a flush; the signal to load
in this way is now localized to where the
unit of work actually needs to load that
way. Note that the UOW does
sometimes load these collections before
the before_update() event is called,
so the usage of "passive_updates" or not
can affect whether or not a collection will
represent the "old" or "new" data, when
accessed within a flush event, based
on when the lazy load was emitted.
The change is backwards incompatible in
the exceedingly small chance that
user event code depended on the old
behavior. [ticket:2350]
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 5 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 18 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 9 |
3 files changed, 20 insertions, 12 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 08e536f71..9b0b35e28 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -84,6 +84,11 @@ NON_PERSISTENT_OK = util.symbol("NON_PERSISTENT_OK", canonical=16 ) +LOAD_AGAINST_COMMITTED = util.symbol("LOAD_AGAINST_COMMITTED", +"""callables should use committed values as primary/foreign keys during a load""", +canonical=32 +) + # pre-packaged sets of flags used as inputs PASSIVE_OFF = util.symbol("PASSIVE_OFF", "Callables can be emitted in all cases.", diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index ec8ee9108..0b73f9b7a 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -377,7 +377,8 @@ class LazyLoader(AbstractRelationshipLoader): def lazy_clause(self, state, reverse_direction=False, alias_secondary=False, - adapt_source=None): + adapt_source=None, + passive=None): if state is None: return self._lazy_none_clause( reverse_direction, @@ -399,14 +400,13 @@ class LazyLoader(AbstractRelationshipLoader): else: mapper = self.parent_property.parent - o = state.obj() # strong ref + o = state.obj() # strong ref dict_ = attributes.instance_dict(o) # use the "committed state" only if we're in a flush # for this state. - sess = _state_session(state) - if sess is not None and sess._flushing: + if passive and passive & attributes.LOAD_AGAINST_COMMITTED: def visit_bindparam(bindparam): if bindparam._identifying_key in bind_to_col: bindparam.callable = \ @@ -428,7 +428,7 @@ class LazyLoader(AbstractRelationshipLoader): traverse(criterion) criterion = visitors.cloned_traverse( - criterion, {}, {'bindparam':visit_bindparam}) + criterion, {}, {'bindparam': visit_bindparam}) if adapt_source: criterion = adapt_source(criterion) @@ -505,12 +505,12 @@ class LazyLoader(AbstractRelationshipLoader): not passive & attributes.RELATED_OBJECT_OK: return attributes.PASSIVE_NO_RESULT - return self._emit_lazyload(session, state, ident_key) + return self._emit_lazyload(session, state, ident_key, passive) def _get_ident_for_use_get(self, session, state, passive): instance_mapper = state.manager.mapper - if session._flushing: + if passive & attributes.LOAD_AGAINST_COMMITTED: get_attr = instance_mapper._get_committed_state_attr_by_column else: get_attr = instance_mapper._get_state_attr_by_column @@ -526,7 +526,7 @@ class LazyLoader(AbstractRelationshipLoader): for pk in self.mapper.primary_key ] - def _emit_lazyload(self, session, state, ident_key): + def _emit_lazyload(self, session, state, ident_key, passive): q = session.query(self.mapper)._adapt_all_clauses() q = q._with_invoke_all_eagers(False) @@ -557,7 +557,7 @@ class LazyLoader(AbstractRelationshipLoader): not isinstance(rev.strategy, LazyLoader): q = q.options(EagerLazyOption((rev.key,), lazy='select')) - lazy_clause = self.lazy_clause(state) + lazy_clause = self.lazy_clause(state, passive=passive) if pending: bind_values = sql_util.bind_values(lazy_clause) diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 84c9f647c..5fb7a55e5 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -178,7 +178,8 @@ class UOWTransaction(object): and passive & attributes.SQL_OK: impl = state.manager[key].impl history = impl.get_history(state, state.dict, - attributes.PASSIVE_OFF) + attributes.PASSIVE_OFF | + attributes.LOAD_AGAINST_COMMITTED) if history and impl.uses_objects: state_history = history.as_state() else: @@ -188,12 +189,14 @@ class UOWTransaction(object): impl = state.manager[key].impl # TODO: store the history as (state, object) tuples # so we don't have to keep converting here - history = impl.get_history(state, state.dict, passive) + history = impl.get_history(state, state.dict, passive | + attributes.LOAD_AGAINST_COMMITTED) if history and impl.uses_objects: state_history = history.as_state() else: state_history = history - self.attributes[hashkey] = (history, state_history, passive) + self.attributes[hashkey] = (history, state_history, + passive) return state_history |
