summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-12-07 20:36:01 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-12-07 20:36:01 -0500
commit07cc9e054ae4d5bb9cfc3c1d807b2a0d58a95b69 (patch)
tree67d674975985cd2ebce1f54b928759024fe2e56e
parentc42b8f8eb8f4c324e2469bf3baaa316c214abce5 (diff)
downloadsqlalchemy-07cc9e054ae4d5bb9cfc3c1d807b2a0d58a95b69.tar.gz
- add an option for bulk_save -> update to not do history
-rw-r--r--lib/sqlalchemy/orm/persistence.py9
-rw-r--r--lib/sqlalchemy/orm/session.py32
-rw-r--r--test/orm/test_bulk.py31
3 files changed, 58 insertions, 14 deletions
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index d94fbb040..f477e1dd7 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -75,7 +75,8 @@ def _bulk_insert(
)
-def _bulk_update(mapper, mappings, session_transaction, isstates):
+def _bulk_update(mapper, mappings, session_transaction,
+ isstates, update_changed_only):
base_mapper = mapper.base_mapper
cached_connections = _cached_connection_dict(base_mapper)
@@ -88,7 +89,10 @@ def _bulk_update(mapper, mappings, session_transaction, isstates):
)
if isstates:
- mappings = [_changed_dict(mapper, state) for state in mappings]
+ if update_changed_only:
+ mappings = [_changed_dict(mapper, state) for state in mappings]
+ else:
+ mappings = [state.dict for state in mappings]
else:
mappings = list(mappings)
@@ -612,6 +616,7 @@ def _emit_update_statements(base_mapper, uowtransaction,
rows = 0
records = list(records)
+
if hasvalue:
for state, state_dict, params, mapper, \
connection, value_params in records:
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index e07b4554e..72d393f54 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -2047,7 +2047,8 @@ class Session(_SessionClassMethods):
with util.safe_reraise():
transaction.rollback(_capture_exception=True)
- def bulk_save_objects(self, objects, return_defaults=False):
+ def bulk_save_objects(
+ self, objects, return_defaults=False, update_changed_only=True):
"""Perform a bulk save of the given list of objects.
The bulk save feature allows mapped objects to be used as the
@@ -2083,12 +2084,13 @@ class Session(_SessionClassMethods):
attribute set, then the object is assumed to be "detached" and
will result in an UPDATE. Otherwise, an INSERT is used.
- In the case of an UPDATE, **all** those attributes which are present
- and are not part of the primary key are applied to the SET clause
- of the UPDATE statement, regardless of whether any change in state
- was logged on each attribute; there is no checking of per-attribute
- history. The primary key attributes, which are required,
- are applied to the WHERE clause.
+ In the case of an UPDATE, statements are grouped based on which
+ attributes have changed, and are thus to be the subject of each
+ SET clause. If ``update_changed_only`` is False, then all
+ attributes present within each object are applied to the UPDATE
+ statement, which may help in allowing the statements to be grouped
+ together into a larger executemany(), and will also reduce the
+ overhead of checking history on attributes.
:param return_defaults: when True, rows that are missing values which
generate defaults, namely integer primary key defaults and sequences,
@@ -2099,6 +2101,11 @@ class Session(_SessionClassMethods):
return_defaults mode greatly reduces the performance gains of the
method overall.
+ :param update_changed_only: when True, UPDATE statements are rendered
+ based on those attributes in each state that have logged changes.
+ When False, all attributes present are rendered into the SET clause
+ with the exception of primary key attributes.
+
.. seealso::
:ref:`bulk_operations`
@@ -2113,7 +2120,8 @@ class Session(_SessionClassMethods):
lambda state: (state.mapper, state.key is not None)
):
self._bulk_save_mappings(
- mapper, states, isupdate, True, return_defaults)
+ mapper, states, isupdate, True,
+ return_defaults, update_changed_only)
def bulk_insert_mappings(self, mapper, mappings, return_defaults=False):
"""Perform a bulk insert of the given list of mapping dictionaries.
@@ -2218,10 +2226,11 @@ class Session(_SessionClassMethods):
:meth:`.Session.bulk_save_objects`
"""
- self._bulk_save_mappings(mapper, mappings, True, False, False)
+ self._bulk_save_mappings(mapper, mappings, True, False, False, False)
def _bulk_save_mappings(
- self, mapper, mappings, isupdate, isstates, return_defaults):
+ self, mapper, mappings, isupdate, isstates,
+ return_defaults, update_changed_only):
mapper = _class_to_mapper(mapper)
self._flushing = True
@@ -2230,7 +2239,8 @@ class Session(_SessionClassMethods):
try:
if isupdate:
persistence._bulk_update(
- mapper, mappings, transaction, isstates)
+ mapper, mappings, transaction,
+ isstates, update_changed_only)
else:
persistence._bulk_insert(
mapper, mappings, transaction, isstates, return_defaults)
diff --git a/test/orm/test_bulk.py b/test/orm/test_bulk.py
index f6d2513d1..e27d3b73c 100644
--- a/test/orm/test_bulk.py
+++ b/test/orm/test_bulk.py
@@ -13,7 +13,7 @@ class BulkTest(testing.AssertsExecutionResults):
run_define_tables = 'each'
-class BulkInsertTest(BulkTest, _fixtures.FixtureTest):
+class BulkInsertUpdateTest(BulkTest, _fixtures.FixtureTest):
@classmethod
def setup_mappers(cls):
@@ -75,6 +75,35 @@ class BulkInsertTest(BulkTest, _fixtures.FixtureTest):
)
assert 'id' not in objects[0].__dict__
+ def test_bulk_save_updated_include_unchanged(self):
+ User, = self.classes("User",)
+
+ s = Session(expire_on_commit=False)
+ objects = [
+ User(name="u1"),
+ User(name="u2"),
+ User(name="u3")
+ ]
+ s.add_all(objects)
+ s.commit()
+
+ objects[0].name = 'u1new'
+ objects[2].name = 'u3new'
+
+ s = Session()
+ with self.sql_execution_asserter() as asserter:
+ s.bulk_save_objects(objects, update_changed_only=False)
+
+ asserter.assert_(
+ CompiledSQL(
+ "UPDATE users SET id=:id, name=:name WHERE "
+ "users.id = :users_id",
+ [{'users_id': 1, 'id': 1, 'name': 'u1new'},
+ {'users_id': 2, 'id': 2, 'name': 'u2'},
+ {'users_id': 3, 'id': 3, 'name': 'u3new'}]
+ )
+ )
+
class BulkInheritanceTest(BulkTest, fixtures.MappedTest):
@classmethod