PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/SQLAlchemy-0.7.8/test/orm/test_pickled.py

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