self.session._autoflush()
return self.session.scalar(s, params=self._params, mapper=self._mapper_zero())
- def delete(self, synchronize_session='fetch'):
+ def delete(self, synchronize_session='evaluate'):
"""Perform a bulk delete query.
Deletes rows matched by this query from the database.
False
don't synchronize the session. This option is the most efficient and is reliable
- once the session is expired, which typically occurs after a commit(). Before
- the expiration, objects may still remain in the session which were in fact deleted
- which can lead to confusing results if they are accessed via get() or already
- loaded collections.
+ once the session is expired, which typically occurs after a commit(), or explicitly
+ using expire_all(). Before the expiration, objects may still remain in the session
+ which were in fact deleted which can lead to confusing results if they are accessed
+ via get() or already loaded collections.
'fetch'
performs a select query before the delete to find objects that are matched
by the delete query and need to be removed from the session. Matched objects
- are removed from the session. 'fetch' is the default strategy.
+ are removed from the session.
'evaluate'
experimental feature. Tries to evaluate the querys criteria in Python
evaluator_compiler = evaluator.EvaluatorCompiler()
eval_condition = evaluator_compiler.process(self.whereclause)
except evaluator.UnevaluatableError:
- synchronize_session = 'fetch'
+ raise sa_exc.InvalidRequestError("Could not evaluate current criteria in Python. "
+ "Specify 'fetch' or False for the synchronize_session parameter.")
delete_stmt = sql.delete(primary_table, context.whereclause)
return result.rowcount
- def update(self, values, synchronize_session='expire'):
+ def update(self, values, synchronize_session='evaluate'):
"""Perform a bulk update query.
Updates rows matched by this query in the database.
attributes on objects in the session. Valid values are:
False
- don't synchronize the session. Use this when you don't need to use the
- session after the update or you can be sure that none of the matched objects
- are in the session.
-
- 'expire'
+ don't synchronize the session. This option is the most efficient and is reliable
+ once the session is expired, which typically occurs after a commit(), or explicitly
+ using expire_all(). Before the expiration, updated objects may still remain in the session
+ with stale values on their attributes, which can lead to confusing results.
+
+ 'fetch'
performs a select query before the update to find objects that are matched
by the update query. The updated attributes are expired on matched objects.
'evaluate'
- experimental feature. Tries to evaluate the querys criteria in Python
+ Tries to evaluate the Query's criteria in Python
straight on the objects in the session. If evaluation of the criteria isn't
- implemented, the 'expire' strategy will be used as a fallback.
+ implemented, an exception is raised.
The expression evaluator currently doesn't account for differing string
collations between the database and Python.
The method does *not* offer in-Python cascading of relations - it is assumed that
ON UPDATE CASCADE is configured for any foreign key references which require it.
- The Session needs to be expired (occurs automatically after commit(), or call expire_all())
- in order for the state of dependent objects subject foreign key cascade to be
- correctly represented.
Also, the ``before_update()`` and ``after_update()`` :class:`~sqlalchemy.orm.interfaces.MapperExtension`
methods are not called from this method. For an update hook here, use the
#TODO: updates of manytoone relations need to be converted to fk assignments
#TODO: cascades need handling.
- if synchronize_session not in [False, 'evaluate', 'expire']:
- raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'expire'")
+ if synchronize_session not in [False, 'evaluate', 'fetch']:
+ raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'fetch'")
context = self._compile_context()
if len(context.statement.froms) != 1 or not isinstance(context.statement.froms[0], schema.Table):
key = expression._column_as_key(key)
value_evaluators[key] = evaluator_compiler.process(expression._literal_as_binds(value))
except evaluator.UnevaluatableError:
- synchronize_session = 'expire'
+ raise sa_exc.InvalidRequestError("Could not evaluate current criteria in Python. "
+ "Specify 'fetch' or False for the synchronize_session parameter.")
update_stmt = sql.update(primary_table, context.whereclause, values)
- if synchronize_session == 'expire':
+ if synchronize_session == 'fetch':
select_stmt = context.statement.with_only_columns(primary_table.primary_key)
matched_rows = session.execute(select_stmt, params=self._params).fetchall()
# expire attributes with pending changes (there was no autoflush, so they are overwritten)
state.expire_attributes(set(evaluated_keys).difference(to_evaluate))
- elif synchronize_session == 'expire':
+ elif synchronize_session == 'fetch':
target_mapper = self._mapper_zero()
for primary_key in matched_rows:
sess = create_session(bind=testing.db, autocommit=False)
john,jack,jill,jane = sess.query(User).order_by(User.id).all()
- sess.query(User).filter('name = :name').params(name='john').delete()
+ sess.query(User).filter('name = :name').params(name='john').delete('fetch')
assert john not in sess
eq_(sess.query(User).order_by(User.id).all(), [jack,jill,jane])
@testing.fails_on('mysql', 'FIXME: unknown')
@testing.resolve_artifact_names
- def test_delete_fallback(self):
+ def test_delete_invalid_evaluation(self):
sess = create_session(bind=testing.db, autocommit=False)
john,jack,jill,jane = sess.query(User).order_by(User.id).all()
- sess.query(User).filter(User.name == select([func.max(User.name)])).delete(synchronize_session='evaluate')
+
+ self.assertRaises(sa_exc.InvalidRequestError,
+ sess.query(User).filter(User.name == select([func.max(User.name)])).delete, synchronize_session='evaluate'
+ )
+
+ sess.query(User).filter(User.name == select([func.max(User.name)])).delete(synchronize_session='fetch')
assert john not in sess
john,jack,jill,jane = sess.query(User).order_by(User.id).all()
- sess.query(User).filter('age > :x').params(x=29).update({'age': User.age - 10}, synchronize_session='evaluate')
+ sess.query(User).filter('age > :x').params(x=29).update({'age': User.age - 10}, synchronize_session='fetch')
eq_([john.age, jack.age, jill.age, jane.age], [25,37,29,27])
eq_(sess.query(User.age).order_by(User.id).all(), zip([25,37,29,27]))
sess = create_session(bind=testing.db, autocommit=False)
john,jack,jill,jane = sess.query(User).order_by(User.id).all()
- sess.query(User).filter(User.age > 29).update({'age': User.age - 10}, synchronize_session='expire')
+ sess.query(User).filter(User.age > 29).update({'age': User.age - 10}, synchronize_session='fetch')
eq_([john.age, jack.age, jill.age, jane.age], [25,37,29,27])
eq_(sess.query(User.age).order_by(User.id).all(), zip([25,37,29,27]))