diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-02-03 13:29:06 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-02-04 10:34:25 -0500 |
commit | 11845453d76e1576f637161e660160f0a6117af6 (patch) | |
tree | 46365ae7f40789150ce358689e972fd1d54191c7 /lib/sqlalchemy | |
parent | 025cf864419051de63f8c86c39a87c05ddbd8a65 (diff) | |
download | sqlalchemy-11845453d76e1576f637161e660160f0a6117af6.tar.gz |
Add bulk_replace to AssociationSet, AssociationDict
Implemented a more comprehensive assignment operation (e.g. "bulk replace")
when using association proxy with sets or dictionaries. Fixes the problem
of redundant proxy objects being created to replace the old ones, which
leads to excessive events and SQL and in the case of unique constraints
will cause the flush to fail.
Fixes: #2642
Change-Id: I57ab27dd9feba057e539267722cce92254fca777
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/ext/associationproxy.py | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index 12e6c1f52..873145b4e 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -560,8 +560,7 @@ class AssociationProxyInstance(object): proxy = self.get(obj) assert self.collection_class is not None if proxy is not values: - proxy.clear() - self._set(proxy, values) + proxy._bulk_replace(self, values) def delete(self, obj): if self.owning_class is None: @@ -959,6 +958,10 @@ class _AssociationCollection(object): self.lazy_collection = state["lazy_collection"] self.parent._inflate(self) + def _bulk_replace(self, assoc_proxy, values): + self.clear() + assoc_proxy._set(self, values) + class _AssociationList(_AssociationCollection): """Generic, converting, list-to-list proxy.""" @@ -1310,6 +1313,21 @@ class _AssociationDict(_AssociationCollection): for key, value in kw: self[key] = value + def _bulk_replace(self, assoc_proxy, values): + existing = set(self) + constants = existing.intersection(values or ()) + additions = set(values or ()).difference(constants) + removals = existing.difference(constants) + + for key, member in values.items() or (): + if key in additions: + self[key] = member + elif key in constants: + self[key] = member + + for key in removals: + del self[key] + def copy(self): return dict(self.items()) @@ -1394,6 +1412,24 @@ class _AssociationSet(_AssociationCollection): for value in other: self.add(value) + def _bulk_replace(self, assoc_proxy, values): + existing = set(self) + constants = existing.intersection(values or ()) + additions = set(values or ()).difference(constants) + removals = existing.difference(constants) + + appender = self.add + remover = self.remove + + for member in values or (): + if member in additions: + appender(member) + elif member in constants: + appender(member) + + for member in removals: + remover(member) + def __ior__(self, other): if not collections._set_binops_check_strict(self, other): return NotImplemented |