summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2016-11-14 20:47:42 -0500
committerGerrit Code Review <gerrit@awstats.zzzcomputing.com>2016-11-14 20:47:42 -0500
commit30abab55726b0a156180c7c3da0eeb8ae61303d5 (patch)
tree5fe75685db773ee4e5cd95dd11f9f3111091681b
parent5def69c0cdc5025b1194e8339446b6e5e528df1e (diff)
parent3e063525f3721ef6fa2c3dd3e6ded67c2fa9cb0b (diff)
downloadsqlalchemy-30abab55726b0a156180c7c3da0eeb8ae61303d5.tar.gz
Merge "Add missing items to collection.__getstate__"
-rw-r--r--doc/build/changelog/changelog_11.rst9
-rw-r--r--lib/sqlalchemy/orm/collections.py8
-rw-r--r--test/orm/test_pickled.py31
3 files changed, 47 insertions, 1 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst
index 1a30e2a4a..dab96fb71 100644
--- a/doc/build/changelog/changelog_11.rst
+++ b/doc/build/changelog/changelog_11.rst
@@ -29,6 +29,15 @@
autoincrement setting (see :ref:`change_3216`) would fail to emit
correctly when invoked upon a lower-case :func:`.table` construct.
+ .. change:: 3852
+ :tags: bug, orm
+ :tickets: 3852
+
+ Fixed regression in collections due to :ticket:`3457` whereby
+ deserialize during pickle or deepcopy would fail to establish all
+ attributes of an ORM collection, causing further mutation operations to
+ fail.
+
.. change:: default_schema
:tags: bug, engine
diff --git a/lib/sqlalchemy/orm/collections.py b/lib/sqlalchemy/orm/collections.py
index 1e022e1dd..47cd774d6 100644
--- a/lib/sqlalchemy/orm/collections.py
+++ b/lib/sqlalchemy/orm/collections.py
@@ -714,12 +714,18 @@ class CollectionAdapter(object):
def __getstate__(self):
return {'key': self._key,
'owner_state': self.owner_state,
- 'data': self.data}
+ 'owner_cls': self.owner_state.class_,
+ 'data': self.data,
+ 'invalidated': self.invalidated}
def __setstate__(self, d):
self._key = d['key']
self.owner_state = d['owner_state']
self._data = weakref.ref(d['data'])
+ self._converter = d['data']._sa_converter
+ d['data']._sa_adapter = self
+ self.invalidated = d['invalidated']
+ self.attr = getattr(d['owner_cls'], self._key).impl
def bulk_replace(values, existing_adapter, new_adapter):
diff --git a/test/orm/test_pickled.py b/test/orm/test_pickled.py
index db2a27c77..f4e49c518 100644
--- a/test/orm/test_pickled.py
+++ b/test/orm/test_pickled.py
@@ -1,6 +1,7 @@
from sqlalchemy.testing import eq_
from sqlalchemy.util import pickle
import sqlalchemy as sa
+import copy
from sqlalchemy import testing
from sqlalchemy.testing.util import picklers
from sqlalchemy.testing import assert_raises_message
@@ -173,6 +174,36 @@ class PickleTest(fixtures.MappedTest):
sess.add(u2)
assert u2.addresses
+ def test_invalidated_flag_pickle(self):
+ users, addresses = (self.tables.users,
+ self.tables.addresses)
+
+ mapper(User, users, properties={
+ 'addresses': relationship(Address, lazy='noload')
+ })
+ mapper(Address, addresses)
+
+ u1 = User()
+ u1.addresses.append(Address())
+ u2 = pickle.loads(pickle.dumps(u1))
+ u2.addresses.append(Address())
+ eq_(len(u2.addresses), 2)
+
+ def test_invalidated_flag_deepcopy(self):
+ users, addresses = (self.tables.users,
+ self.tables.addresses)
+
+ mapper(User, users, properties={
+ 'addresses': relationship(Address, lazy='noload')
+ })
+ mapper(Address, addresses)
+
+ u1 = User()
+ u1.addresses.append(Address())
+ u2 = copy.deepcopy(u1)
+ u2.addresses.append(Address())
+ eq_(len(u2.addresses), 2)
+
@testing.requires.non_broken_pickle
def test_instance_deferred_cols(self):
users, addresses = (self.tables.users,