From: Mike Bayer Date: Sun, 26 Apr 2009 21:57:18 +0000 (+0000) Subject: - Allowed pickling of PropertyOption objects constructed with X-Git-Tag: rel_0_5_4~16 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=85f754751412fd48f915e7c3062689401a3723b8;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Allowed pickling of PropertyOption objects constructed with instrumented descriptors; previously, pickle errors would occur when pickling an object which was loaded with a descriptor-based option, such as query.options(eagerload(MyClass.foo)). --- diff --git a/CHANGES b/CHANGES index 34f0a9afbb..22d4c98434 100644 --- a/CHANGES +++ b/CHANGES @@ -19,7 +19,12 @@ CHANGES be assigned to a pending parent instance, otherwise modified events would not be fired correctly. Set collection is now compatible with merge(), fixes [ticket:1352]. - + + - Allowed pickling of PropertyOption objects constructed with + instrumented descriptors; previously, pickle errors would occur + when pickling an object which was loaded with a descriptor-based + option, such as query.options(eagerload(MyClass.foo)). + - Lazy loader will not use get() if the "lazy load" SQL clause matches the clause used by get(), but contains some parameters hardcoded. Previously the lazy strategy would fail with the diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 3b7507def6..d36f51194e 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -682,6 +682,27 @@ class PropertyOption(MapperOption): else: return None + def __getstate__(self): + d = self.__dict__.copy() + d['key'] = ret = [] + for token in util.to_list(self.key): + if isinstance(token, PropComparator): + ret.append((token.mapper.class_, token.key)) + else: + ret.append(token) + return d + + def __setstate__(self, state): + ret = [] + for key in state['key']: + if isinstance(key, tuple): + cls, propkey = key + ret.append(getattr(cls, propkey)) + else: + ret.append(key) + state['key'] = tuple(ret) + self.__dict__ = state + def __get_paths(self, query, raiseerr): path = None entity = None diff --git a/test/orm/pickled.py b/test/orm/pickled.py index ca308bb5be..878fe931e3 100644 --- a/test/orm/pickled.py +++ b/test/orm/pickled.py @@ -99,7 +99,36 @@ class PickleTest(_fixtures.FixtureTest): self.assertEquals(ad.email_address, 'ed@bar.com') self.assertEquals(u2, User(name='ed', addresses=[Address(email_address='ed@bar.com')])) + @testing.resolve_artifact_names + def test_options_with_descriptors(self): + mapper(User, users, properties={ + 'addresses':relation(Address, backref="user") + }) + mapper(Address, addresses) + sess = create_session() + u1 = User(name='ed') + u1.addresses.append(Address(email_address='ed@bar.com')) + sess.add(u1) + sess.flush() + sess.expunge_all() + for opt in [ + sa.orm.eagerload(User.addresses), + sa.orm.eagerload("addresses"), + sa.orm.defer("name"), + sa.orm.defer(User.name), + sa.orm.defer([User.name]), + sa.orm.eagerload("addresses", User.addresses), + sa.orm.eagerload(["addresses", User.addresses]), + ]: + opt2 = pickle.loads(pickle.dumps(opt)) + self.assertEquals(opt.key, opt2.key) + + u1 = sess.query(User).options(opt).first() + + u2 = pickle.loads(pickle.dumps(u1)) + + class PolymorphicDeferredTest(_base.MappedTest): def define_tables(self, metadata): Table('users', metadata,