diff options
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/orm/properties.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 25 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/session.py | 17 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 2 |
5 files changed, 38 insertions, 13 deletions
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 1ce71fdb6..b0eb54301 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -480,7 +480,8 @@ class PropertyLoader(StrategizedProperty): def cascade_iterator(self, type_, state, visited_instances, halt_on=None): if not type_ in self.cascade: return - passive = type_ != 'delete' or self.passive_deletes + # only actively lazy load on the 'delete' cascade + passive = type_ != 'delete' or self.passive_deletes mapper = self.mapper.primary_mapper() instances = state.value_as_iterable(self.key, passive=passive) if instances: diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 07caae07a..2fba04ee0 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1044,7 +1044,7 @@ class Query(object): def _execute_and_instances(self, querycontext): result = self.session.execute(querycontext.statement, params=self._params, mapper=self._mapper_zero_or_none(), _state=self._refresh_state) return self.instances(result, querycontext) - + def instances(self, cursor, __context=None): """Given a ResultProxy cursor as returned by connection.execute(), return an ORM result as an iterator. @@ -1081,7 +1081,7 @@ class Query(object): labels = dict([(label, property(util.itemgetter(i))) for i, label in enumerate(labels) if label]) rowtuple = type.__new__(type, "RowTuple", (tuple,), labels) rowtuple.keys = labels.keys - + while True: context.progress = util.Set() context.partials = {} @@ -1165,7 +1165,11 @@ class Query(object): if lockmode is not None: q._lockmode = lockmode - q.__get_options(populate_existing=bool(refresh_state), version_check=(lockmode is not None), only_load_props=only_load_props, refresh_state=refresh_state) + q.__get_options( + populate_existing=bool(refresh_state), + version_check=(lockmode is not None), + only_load_props=only_load_props, + refresh_state=refresh_state) q._order_by = None try: # call using all() to avoid LIMIT compilation complexity @@ -1174,7 +1178,13 @@ class Query(object): return None def _select_args(self): - return {'limit':self._limit, 'offset':self._offset, 'distinct':self._distinct, 'group_by':self._group_by or None, 'having':self._having or None} + return { + 'limit':self._limit, + 'offset':self._offset, + 'distinct':self._distinct, + 'group_by':self._group_by or None, + 'having':self._having or None + } _select_args = property(_select_args) def _should_nest_selectable(self): @@ -1343,6 +1353,13 @@ class Query(object): self._adjust_for_single_inheritance(context) + if not context.primary_columns: + if self._only_load_props: + raise sa_exc.InvalidRequestError("No column-based properties specified for refresh operation." + " Use session.expire() to reload collections and related items.") + else: + raise sa_exc.InvalidRequestError("Query contains no columns with which to SELECT from.") + if eager_joins and self._should_nest_selectable: # for eager joins present and LIMIT/OFFSET/DISTINCT, wrap the query inside a select, # then append eager joins onto that diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 2110f8826..b9f170733 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -960,7 +960,7 @@ class Session(object): # remove associations cascaded = list(_cascade_state_iterator('refresh-expire', state)) _expire_state(state, None) - for (state, m) in cascaded: + for (state, m, o) in cascaded: _expire_state(state, None) def prune(self): @@ -991,7 +991,7 @@ class Session(object): raise sa_exc.InvalidRequestError( "Instance %s is not present in this Session" % mapperutil.state_str(state)) - for s, m in [(state, None)] + list(_cascade_state_iterator('expunge', state)): + for s, m, o in [(state, None, None)] + list(_cascade_state_iterator('expunge', state)): self._expunge_state(s) def _expunge_state(self, state): @@ -1120,8 +1120,12 @@ class Session(object): state = attributes.instance_state(instance) except exc.NO_STATE: raise exc.UnmappedInstanceError(instance) - self._delete_impl(state) - for state, m in _cascade_state_iterator('delete', state): + + # grab the full cascade list first, since lazyloads/autoflush + # may be triggered by this operation (delete cascade lazyloads by default) + cascade_states = list(_cascade_state_iterator('delete', state)) + self._delete_impl(state) + for state, m, o in cascade_states: self._delete_impl(state, ignore_transient=True) def merge(self, instance, entity_name=None, dont_load=False, @@ -1508,8 +1512,11 @@ _sessions = weakref.WeakValueDictionary() def _cascade_state_iterator(cascade, state, **kwargs): mapper = _state_mapper(state) + # yield the state, object, mapper. yielding the object + # allows the iterator's results to be held in a list without + # states being garbage collected for (o, m) in mapper.cascade_iterator(cascade, state, **kwargs): - yield attributes.instance_state(o), m + yield attributes.instance_state(o), o, m def _cascade_unknown_state_iterator(cascade, state, **kwargs): mapper = _state_mapper(state) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index fcb56865b..3073e17db 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -524,8 +524,8 @@ class LoadLazyAttribute(object): "lazy load operation of attribute '%s' cannot proceed" % (mapperutil.state_str(state), self.key) ) - - q = session.query(prop.mapper).autoflush(False)._adapt_all_clauses() + + q = session.query(prop.mapper)._adapt_all_clauses() if self.path: q = q._with_current_path(self.path) diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 51ad1dfb0..3b82fbdd0 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -788,7 +788,7 @@ class SchemaGenerator(DDLBase): if column.default is not None: self.traverse_single(column.default) - self.append("\nCREATE " + " ".join(table._prefixes) + " TABLE " + self.preparer.format_table(table) + " (") + self.append("\nCREATE" + " ".join(table._prefixes) + " TABLE " + self.preparer.format_table(table) + " (") separator = "\n" |