]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add missing items to collection.__getstate__
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 14 Nov 2016 21:09:13 +0000 (16:09 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 14 Nov 2016 21:25:47 +0000 (16:25 -0500)
the refactor in b606e47ddc54 / ticket:3457 failed
to adjust __getstate__ / __setstate__.  need to memoize
a few more things including the class itself so that we
can navigate back to "attr".

Change-Id: I4ece2a616cb8b9dac7b50763ca59e47d0f26cfdf
Fixes: #3852
doc/build/changelog/changelog_11.rst
lib/sqlalchemy/orm/collections.py
test/orm/test_pickled.py

index 1a30e2a4ac0c127fc3270e953eca569fa0cf2c4c..dab96fb71c0e6d98a43c393bdc9eca7ae8ef1008 100644 (file)
         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
 
index 1e022e1dd811fb7c21d64d227cf9c526d1626a29..47cd774d6f7fe7e0ddc09cf36554ffa3fb2fd2da 100644 (file)
@@ -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):
index db2a27c77a1b0c81c575c2409b9f8edcfa9baafc..f4e49c518a4627c96ce476d323d035c03ee9b6ad 100644 (file)
@@ -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,