/SQLAlchemy-0.7.8/test/orm/test_pickled.py
Python | 552 lines | 454 code | 85 blank | 13 comment | 20 complexity | 0edfb2f6cdaf60fda2506194bc9f42d5 MD5 | raw file
1from test.lib.testing import eq_
2from sqlalchemy.util import pickle
3import sqlalchemy as sa
4from test.lib import testing
5from test.lib.util import picklers
6from test.lib.testing import assert_raises_message
7from sqlalchemy import Integer, String, ForeignKey, exc, MetaData
8from test.lib.schema import Table, Column
9from sqlalchemy.orm import mapper, relationship, create_session, \
10 sessionmaker, attributes, interfaces,\
11 clear_mappers, exc as orm_exc,\
12 configure_mappers, Session, lazyload_all,\
13 lazyload, aliased
14from sqlalchemy.orm.collections import attribute_mapped_collection, \
15 column_mapped_collection
16from test.lib import fixtures
17from test.orm import _fixtures
18from test.lib.pickleable import User, Address, Dingaling, Order, \
19 Child1, Child2, Parent, Screen, EmailUser
20
21
22class PickleTest(fixtures.MappedTest):
23 @classmethod
24 def define_tables(cls, metadata):
25 Table('users', metadata,
26 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
27 Column('name', String(30), nullable=False),
28 test_needs_acid=True,
29 test_needs_fk=True
30 )
31
32 Table('addresses', metadata,
33 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
34 Column('user_id', None, ForeignKey('users.id')),
35 Column('email_address', String(50), nullable=False),
36 test_needs_acid=True,
37 test_needs_fk=True
38 )
39 Table('orders', metadata,
40 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
41 Column('user_id', None, ForeignKey('users.id')),
42 Column('address_id', None, ForeignKey('addresses.id')),
43 Column('description', String(30)),
44 Column('isopen', Integer),
45 test_needs_acid=True,
46 test_needs_fk=True
47 )
48 Table("dingalings", metadata,
49 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
50 Column('address_id', None, ForeignKey('addresses.id')),
51 Column('data', String(30)),
52 test_needs_acid=True,
53 test_needs_fk=True
54 )
55
56
57 def test_transient(self):
58 users, addresses = (self.tables.users,
59 self.tables.addresses)
60
61 mapper(User, users, properties={
62 'addresses':relationship(Address, backref="user")
63 })
64 mapper(Address, addresses)
65
66 sess = create_session()
67 u1 = User(name='ed')
68 u1.addresses.append(Address(email_address='ed@bar.com'))
69
70 u2 = pickle.loads(pickle.dumps(u1))
71 sess.add(u2)
72 sess.flush()
73
74 sess.expunge_all()
75
76 eq_(u1, sess.query(User).get(u2.id))
77
78 def test_no_mappers(self):
79 users = self.tables.users
80
81
82 umapper = mapper(User, users)
83 u1 = User(name='ed')
84 u1_pickled = pickle.dumps(u1, -1)
85
86 clear_mappers()
87
88 assert_raises_message(
89 orm_exc.UnmappedInstanceError,
90 "Cannot deserialize object of type <class 'test.lib.pickleable.User'> - no mapper()",
91 pickle.loads, u1_pickled)
92
93 def test_no_instrumentation(self):
94 users = self.tables.users
95
96
97 umapper = mapper(User, users)
98 u1 = User(name='ed')
99 u1_pickled = pickle.dumps(u1, -1)
100
101 clear_mappers()
102
103 umapper = mapper(User, users)
104
105 u1 = pickle.loads(u1_pickled)
106 # this fails unless the InstanceState
107 # compiles the mapper
108 eq_(str(u1), "User(name='ed')")
109
110 def test_serialize_path(self):
111 users, addresses = (self.tables.users,
112 self.tables.addresses)
113
114 umapper = mapper(User, users, properties={
115 'addresses':relationship(Address, backref="user")
116 })
117 amapper = mapper(Address, addresses)
118
119 # this is a "relationship" path with mapper, key, mapper, key
120 p1 = (umapper, 'addresses', amapper, 'email_address')
121 eq_(
122 interfaces.deserialize_path(interfaces.serialize_path(p1)),
123 p1
124 )
125
126 # this is a "mapper" path with mapper, key, mapper, no key
127 # at the end.
128 p2 = (umapper, 'addresses', amapper, )
129 eq_(
130 interfaces.deserialize_path(interfaces.serialize_path(p2)),
131 p2
132 )
133
134 # test a blank path
135 p3 = ()
136 eq_(
137 interfaces.deserialize_path(interfaces.serialize_path(p3)),
138 p3
139 )
140
141 def test_class_deferred_cols(self):
142 addresses, users = (self.tables.addresses,
143 self.tables.users)
144
145 mapper(User, users, properties={
146 'name':sa.orm.deferred(users.c.name),
147 'addresses':relationship(Address, backref="user")
148 })
149 mapper(Address, addresses, properties={
150 'email_address':sa.orm.deferred(addresses.c.email_address)
151 })
152 sess = create_session()
153 u1 = User(name='ed')
154 u1.addresses.append(Address(email_address='ed@bar.com'))
155 sess.add(u1)
156 sess.flush()
157 sess.expunge_all()
158 u1 = sess.query(User).get(u1.id)
159 assert 'name' not in u1.__dict__
160 assert 'addresses' not in u1.__dict__
161
162 u2 = pickle.loads(pickle.dumps(u1))
163 sess2 = create_session()
164 sess2.add(u2)
165 eq_(u2.name, 'ed')
166 eq_(u2, User(name='ed', addresses=[Address(email_address='ed@bar.com')]))
167
168 u2 = pickle.loads(pickle.dumps(u1))
169 sess2 = create_session()
170 u2 = sess2.merge(u2, load=False)
171 eq_(u2.name, 'ed')
172 eq_(u2, User(name='ed', addresses=[Address(email_address='ed@bar.com')]))
173
174 def test_instance_lazy_relation_loaders(self):
175 users, addresses = (self.tables.users,
176 self.tables.addresses)
177
178 mapper(User, users, properties={
179 'addresses':relationship(Address, lazy='noload')
180 })
181 mapper(Address, addresses)
182
183 sess = Session()
184 u1 = User(name='ed', addresses=[
185 Address(
186 email_address='ed@bar.com',
187 )
188 ])
189
190 sess.add(u1)
191 sess.commit()
192 sess.close()
193
194 u1 = sess.query(User).options(
195 lazyload(User.addresses)
196 ).first()
197 u2 = pickle.loads(pickle.dumps(u1))
198
199 sess = Session()
200 sess.add(u2)
201 assert u2.addresses
202
203 def test_instance_deferred_cols(self):
204 users, addresses = (self.tables.users,
205 self.tables.addresses)
206
207 mapper(User, users, properties={
208 'addresses':relationship(Address, backref="user")
209 })
210 mapper(Address, addresses)
211
212 sess = create_session()
213 u1 = User(name='ed')
214 u1.addresses.append(Address(email_address='ed@bar.com'))
215 sess.add(u1)
216 sess.flush()
217 sess.expunge_all()
218
219 u1 = sess.query(User).\
220 options(sa.orm.defer('name'),
221 sa.orm.defer('addresses.email_address')).\
222 get(u1.id)
223 assert 'name' not in u1.__dict__
224 assert 'addresses' not in u1.__dict__
225
226 u2 = pickle.loads(pickle.dumps(u1))
227 sess2 = create_session()
228 sess2.add(u2)
229 eq_(u2.name, 'ed')
230 assert 'addresses' not in u2.__dict__
231 ad = u2.addresses[0]
232 assert 'email_address' not in ad.__dict__
233 eq_(ad.email_address, 'ed@bar.com')
234 eq_(u2, User(name='ed', addresses=[Address(email_address='ed@bar.com')]))
235
236 u2 = pickle.loads(pickle.dumps(u1))
237 sess2 = create_session()
238 u2 = sess2.merge(u2, load=False)
239 eq_(u2.name, 'ed')
240 assert 'addresses' not in u2.__dict__
241 ad = u2.addresses[0]
242
243 # mapper options now transmit over merge(),
244 # new as of 0.6, so email_address is deferred.
245 assert 'email_address' not in ad.__dict__
246
247 eq_(ad.email_address, 'ed@bar.com')
248 eq_(u2, User(name='ed', addresses=[Address(email_address='ed@bar.com')]))
249
250 def test_pickle_protocols(self):
251 users, addresses = (self.tables.users,
252 self.tables.addresses)
253
254 mapper(User, users, properties={
255 'addresses':relationship(Address, backref="user")
256 })
257 mapper(Address, addresses)
258
259 sess = sessionmaker()()
260 u1 = User(name='ed')
261 u1.addresses.append(Address(email_address='ed@bar.com'))
262 sess.add(u1)
263 sess.commit()
264
265 u1 = sess.query(User).first()
266 u1.addresses
267
268 for loads, dumps in picklers():
269 u2 = loads(dumps(u1))
270 eq_(u1, u2)
271
272
273 def test_options_with_descriptors(self):
274 users, addresses, dingalings = (self.tables.users,
275 self.tables.addresses,
276 self.tables.dingalings)
277
278 mapper(User, users, properties={
279 'addresses':relationship(Address, backref="user")
280 })
281 mapper(Address, addresses, properties={
282 'dingaling':relationship(Dingaling)
283 })
284 mapper(Dingaling, dingalings)
285 sess = create_session()
286 u1 = User(name='ed')
287 u1.addresses.append(Address(email_address='ed@bar.com'))
288 sess.add(u1)
289 sess.flush()
290 sess.expunge_all()
291
292 for opt in [
293 sa.orm.joinedload(User.addresses),
294 sa.orm.joinedload("addresses"),
295 sa.orm.defer("name"),
296 sa.orm.defer(User.name),
297 sa.orm.joinedload("addresses", Address.dingaling),
298 ]:
299 opt2 = pickle.loads(pickle.dumps(opt))
300 eq_(opt.key, opt2.key)
301
302 u1 = sess.query(User).options(opt).first()
303 u2 = pickle.loads(pickle.dumps(u1))
304
305 def test_collection_setstate(self):
306 """test a particular cycle that requires CollectionAdapter
307 to not rely upon InstanceState to deserialize."""
308
309 m = MetaData()
310 c1 = Table('c1', m,
311 Column('parent_id', String,
312 ForeignKey('p.id'), primary_key=True)
313 )
314 c2 = Table('c2', m,
315 Column('parent_id', String,
316 ForeignKey('p.id'), primary_key=True)
317 )
318 p = Table('p', m,
319 Column('id', String, primary_key=True)
320 )
321
322 mapper(Parent, p, properties={
323 'children1':relationship(Child1),
324 'children2':relationship(Child2)
325 })
326 mapper(Child1, c1)
327 mapper(Child2, c2)
328
329 obj = Parent()
330 screen1 = Screen(obj)
331 screen1.errors = [obj.children1, obj.children2]
332 screen2 = Screen(Child2(), screen1)
333 pickle.loads(pickle.dumps(screen2))
334
335 def test_exceptions(self):
336 class Foo(object):
337 pass
338 users = self.tables.users
339 mapper(User, users)
340
341 for sa_exc in (
342 orm_exc.UnmappedInstanceError(Foo()),
343 orm_exc.UnmappedClassError(Foo),
344 orm_exc.ObjectDeletedError(attributes.instance_state(User())),
345 ):
346 for loads, dumps in picklers():
347 repickled = loads(dumps(sa_exc))
348 eq_(repickled.args[0], sa_exc.args[0])
349
350 def test_attribute_mapped_collection(self):
351 users, addresses = self.tables.users, self.tables.addresses
352
353 mapper(User, users, properties={
354 'addresses':relationship(
355 Address,
356 collection_class=
357 attribute_mapped_collection('email_address')
358 )
359 })
360 mapper(Address, addresses)
361 u1 = User()
362 u1.addresses = {"email1":Address(email_address="email1")}
363 for loads, dumps in picklers():
364 repickled = loads(dumps(u1))
365 eq_(u1.addresses, repickled.addresses)
366 eq_(repickled.addresses['email1'],
367 Address(email_address="email1"))
368
369 def test_column_mapped_collection(self):
370 users, addresses = self.tables.users, self.tables.addresses
371
372 mapper(User, users, properties={
373 'addresses':relationship(
374 Address,
375 collection_class=
376 column_mapped_collection(
377 addresses.c.email_address)
378 )
379 })
380 mapper(Address, addresses)
381 u1 = User()
382 u1.addresses = {
383 "email1":Address(email_address="email1"),
384 "email2":Address(email_address="email2")
385 }
386 for loads, dumps in picklers():
387 repickled = loads(dumps(u1))
388 eq_(u1.addresses, repickled.addresses)
389 eq_(repickled.addresses['email1'],
390 Address(email_address="email1"))
391
392 def test_composite_column_mapped_collection(self):
393 users, addresses = self.tables.users, self.tables.addresses
394
395 mapper(User, users, properties={
396 'addresses':relationship(
397 Address,
398 collection_class=
399 column_mapped_collection([
400 addresses.c.id,
401 addresses.c.email_address])
402 )
403 })
404 mapper(Address, addresses)
405 u1 = User()
406 u1.addresses = {
407 (1, "email1"):Address(id=1, email_address="email1"),
408 (2, "email2"):Address(id=2, email_address="email2")
409 }
410 for loads, dumps in picklers():
411 repickled = loads(dumps(u1))
412 eq_(u1.addresses, repickled.addresses)
413 eq_(repickled.addresses[(1, 'email1')],
414 Address(id=1, email_address="email1"))
415
416class PolymorphicDeferredTest(fixtures.MappedTest):
417 @classmethod
418 def define_tables(cls, metadata):
419 Table('users', metadata,
420 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
421 Column('name', String(30)),
422 Column('type', String(30)))
423 Table('email_users', metadata,
424 Column('id', Integer, ForeignKey('users.id'), primary_key=True),
425 Column('email_address', String(30)))
426
427
428 def test_polymorphic_deferred(self):
429 email_users, users = (self.tables.email_users,
430 self.tables.users,
431 )
432
433 mapper(User, users, polymorphic_identity='user', polymorphic_on=users.c.type)
434 mapper(EmailUser, email_users, inherits=User, polymorphic_identity='emailuser')
435
436 eu = EmailUser(name="user1", email_address='foo@bar.com')
437 sess = create_session()
438 sess.add(eu)
439 sess.flush()
440 sess.expunge_all()
441
442 eu = sess.query(User).first()
443 eu2 = pickle.loads(pickle.dumps(eu))
444 sess2 = create_session()
445 sess2.add(eu2)
446 assert 'email_address' not in eu2.__dict__
447 eq_(eu2.email_address, 'foo@bar.com')
448
449class TupleLabelTest(_fixtures.FixtureTest):
450 @classmethod
451 def setup_classes(cls):
452 pass
453
454 @classmethod
455 def setup_mappers(cls):
456 users, addresses, orders = cls.tables.users, cls.tables.addresses, cls.tables.orders
457 mapper(User, users, properties={
458 'addresses':relationship(Address, backref='user', order_by=addresses.c.id),
459 'orders':relationship(Order, backref='user', order_by=orders.c.id), # o2m, m2o
460 })
461 mapper(Address, addresses)
462 mapper(Order, orders, properties={
463 'address':relationship(Address), # m2o
464 })
465
466 def test_tuple_labeling(self):
467 users = self.tables.users
468 sess = create_session()
469
470 # test pickle + all the protocols !
471 for pickled in False, -1, 0, 1, 2:
472 for row in sess.query(User, Address).join(User.addresses).all():
473 if pickled is not False:
474 row = pickle.loads(pickle.dumps(row, pickled))
475
476 eq_(row.keys(), ['User', 'Address'])
477 eq_(row.User, row[0])
478 eq_(row.Address, row[1])
479
480 for row in sess.query(User.name, User.id.label('foobar')):
481 if pickled is not False:
482 row = pickle.loads(pickle.dumps(row, pickled))
483 eq_(row.keys(), ['name', 'foobar'])
484 eq_(row.name, row[0])
485 eq_(row.foobar, row[1])
486
487 for row in sess.query(User).values(User.name, User.id.label('foobar')):
488 if pickled is not False:
489 row = pickle.loads(pickle.dumps(row, pickled))
490 eq_(row.keys(), ['name', 'foobar'])
491 eq_(row.name, row[0])
492 eq_(row.foobar, row[1])
493
494 oalias = aliased(Order)
495 for row in sess.query(User, oalias).join(User.orders).all():
496 if pickled is not False:
497 row = pickle.loads(pickle.dumps(row, pickled))
498 eq_(row.keys(), ['User'])
499 eq_(row.User, row[0])
500
501 oalias = aliased(Order, name='orders')
502 for row in sess.query(User, oalias).join(oalias, User.orders).all():
503 if pickled is not False:
504 row = pickle.loads(pickle.dumps(row, pickled))
505 eq_(row.keys(), ['User', 'orders'])
506 eq_(row.User, row[0])
507 eq_(row.orders, row[1])
508
509 # test here that first col is not labeled, only
510 # one name in keys, matches correctly
511 for row in sess.query(User.name + 'hoho', User.name):
512 eq_(row.keys(), ['name'])
513 eq_(row[0], row.name + 'hoho')
514
515 if pickled is not False:
516 ret = sess.query(User, Address).join(User.addresses).all()
517 pickle.loads(pickle.dumps(ret, pickled))
518
519class CustomSetupTeardownTest(fixtures.MappedTest):
520 @classmethod
521 def define_tables(cls, metadata):
522 Table('users', metadata,
523 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
524 Column('name', String(30), nullable=False),
525 test_needs_acid=True,
526 test_needs_fk=True
527 )
528
529 Table('addresses', metadata,
530 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
531 Column('user_id', None, ForeignKey('users.id')),
532 Column('email_address', String(50), nullable=False),
533 test_needs_acid=True,
534 test_needs_fk=True
535 )
536 def test_rebuild_state(self):
537 """not much of a 'test', but illustrate how to
538 remove instance-level state before pickling.
539
540 """
541
542 users = self.tables.users
543
544 mapper(User, users)
545
546 u1 = User()
547 attributes.manager_of_class(User).teardown_instance(u1)
548 assert not u1.__dict__
549 u2 = pickle.loads(pickle.dumps(u1))
550 attributes.manager_of_class(User).setup_instance(u2)
551 assert attributes.instance_state(u2)
552