PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/SQLAlchemy-0.7.8/test/ext/test_associationproxy.py

#
Python | 1369 lines | 1062 code | 290 blank | 17 comment | 51 complexity | bedaf670fede93d244fb797b82068e92 MD5 | raw file
  1. from test.lib.testing import eq_, assert_raises
  2. import copy
  3. import pickle
  4. from sqlalchemy import *
  5. from sqlalchemy.orm import *
  6. from sqlalchemy.orm.collections import collection, attribute_mapped_collection
  7. from sqlalchemy.ext.associationproxy import *
  8. from sqlalchemy.ext.associationproxy import _AssociationList
  9. from test.lib import *
  10. from test.lib.testing import assert_raises_message
  11. from test.lib.util import gc_collect
  12. from sqlalchemy.sql import not_
  13. from test.lib import fixtures
  14. class DictCollection(dict):
  15. @collection.appender
  16. def append(self, obj):
  17. self[obj.foo] = obj
  18. @collection.remover
  19. def remove(self, obj):
  20. del self[obj.foo]
  21. class SetCollection(set):
  22. pass
  23. class ListCollection(list):
  24. pass
  25. class ObjectCollection(object):
  26. def __init__(self):
  27. self.values = list()
  28. @collection.appender
  29. def append(self, obj):
  30. self.values.append(obj)
  31. @collection.remover
  32. def remove(self, obj):
  33. self.values.remove(obj)
  34. def __iter__(self):
  35. return iter(self.values)
  36. class _CollectionOperations(fixtures.TestBase):
  37. def setup(self):
  38. collection_class = self.collection_class
  39. metadata = MetaData(testing.db)
  40. parents_table = Table('Parent', metadata,
  41. Column('id', Integer, primary_key=True,
  42. test_needs_autoincrement=True),
  43. Column('name', String(128)))
  44. children_table = Table('Children', metadata,
  45. Column('id', Integer, primary_key=True,
  46. test_needs_autoincrement=True),
  47. Column('parent_id', Integer,
  48. ForeignKey('Parent.id')),
  49. Column('foo', String(128)),
  50. Column('name', String(128)))
  51. class Parent(object):
  52. children = association_proxy('_children', 'name')
  53. def __init__(self, name):
  54. self.name = name
  55. class Child(object):
  56. if collection_class and issubclass(collection_class, dict):
  57. def __init__(self, foo, name):
  58. self.foo = foo
  59. self.name = name
  60. else:
  61. def __init__(self, name):
  62. self.name = name
  63. mapper(Parent, parents_table, properties={
  64. '_children': relationship(Child, lazy='joined',
  65. collection_class=collection_class)})
  66. mapper(Child, children_table)
  67. metadata.create_all()
  68. self.metadata = metadata
  69. self.session = create_session()
  70. self.Parent, self.Child = Parent, Child
  71. def teardown(self):
  72. self.metadata.drop_all()
  73. def roundtrip(self, obj):
  74. if obj not in self.session:
  75. self.session.add(obj)
  76. self.session.flush()
  77. id, type_ = obj.id, type(obj)
  78. self.session.expunge_all()
  79. return self.session.query(type_).get(id)
  80. def _test_sequence_ops(self):
  81. Parent, Child = self.Parent, self.Child
  82. p1 = Parent('P1')
  83. self.assert_(not p1._children)
  84. self.assert_(not p1.children)
  85. ch = Child('regular')
  86. p1._children.append(ch)
  87. self.assert_(ch in p1._children)
  88. self.assert_(len(p1._children) == 1)
  89. self.assert_(p1.children)
  90. self.assert_(len(p1.children) == 1)
  91. self.assert_(ch not in p1.children)
  92. self.assert_('regular' in p1.children)
  93. p1.children.append('proxied')
  94. self.assert_('proxied' in p1.children)
  95. self.assert_('proxied' not in p1._children)
  96. self.assert_(len(p1.children) == 2)
  97. self.assert_(len(p1._children) == 2)
  98. self.assert_(p1._children[0].name == 'regular')
  99. self.assert_(p1._children[1].name == 'proxied')
  100. del p1._children[1]
  101. self.assert_(len(p1._children) == 1)
  102. self.assert_(len(p1.children) == 1)
  103. self.assert_(p1._children[0] == ch)
  104. del p1.children[0]
  105. self.assert_(len(p1._children) == 0)
  106. self.assert_(len(p1.children) == 0)
  107. p1.children = ['a','b','c']
  108. self.assert_(len(p1._children) == 3)
  109. self.assert_(len(p1.children) == 3)
  110. del ch
  111. p1 = self.roundtrip(p1)
  112. self.assert_(len(p1._children) == 3)
  113. self.assert_(len(p1.children) == 3)
  114. popped = p1.children.pop()
  115. self.assert_(len(p1.children) == 2)
  116. self.assert_(popped not in p1.children)
  117. p1 = self.roundtrip(p1)
  118. self.assert_(len(p1.children) == 2)
  119. self.assert_(popped not in p1.children)
  120. p1.children[1] = 'changed-in-place'
  121. self.assert_(p1.children[1] == 'changed-in-place')
  122. inplace_id = p1._children[1].id
  123. p1 = self.roundtrip(p1)
  124. self.assert_(p1.children[1] == 'changed-in-place')
  125. assert p1._children[1].id == inplace_id
  126. p1.children.append('changed-in-place')
  127. self.assert_(p1.children.count('changed-in-place') == 2)
  128. p1.children.remove('changed-in-place')
  129. self.assert_(p1.children.count('changed-in-place') == 1)
  130. p1 = self.roundtrip(p1)
  131. self.assert_(p1.children.count('changed-in-place') == 1)
  132. p1._children = []
  133. self.assert_(len(p1.children) == 0)
  134. after = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
  135. p1.children = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
  136. self.assert_(len(p1.children) == 10)
  137. self.assert_([c.name for c in p1._children] == after)
  138. p1.children[2:6] = ['x'] * 4
  139. after = ['a', 'b', 'x', 'x', 'x', 'x', 'g', 'h', 'i', 'j']
  140. self.assert_(p1.children == after)
  141. self.assert_([c.name for c in p1._children] == after)
  142. p1.children[2:6] = ['y']
  143. after = ['a', 'b', 'y', 'g', 'h', 'i', 'j']
  144. self.assert_(p1.children == after)
  145. self.assert_([c.name for c in p1._children] == after)
  146. p1.children[2:3] = ['z'] * 4
  147. after = ['a', 'b', 'z', 'z', 'z', 'z', 'g', 'h', 'i', 'j']
  148. self.assert_(p1.children == after)
  149. self.assert_([c.name for c in p1._children] == after)
  150. p1.children[2::2] = ['O'] * 4
  151. after = ['a', 'b', 'O', 'z', 'O', 'z', 'O', 'h', 'O', 'j']
  152. self.assert_(p1.children == after)
  153. self.assert_([c.name for c in p1._children] == after)
  154. assert_raises(TypeError, set, [p1.children])
  155. p1.children *= 0
  156. after = []
  157. self.assert_(p1.children == after)
  158. self.assert_([c.name for c in p1._children] == after)
  159. p1.children += ['a', 'b']
  160. after = ['a', 'b']
  161. self.assert_(p1.children == after)
  162. self.assert_([c.name for c in p1._children] == after)
  163. p1.children += ['c']
  164. after = ['a', 'b', 'c']
  165. self.assert_(p1.children == after)
  166. self.assert_([c.name for c in p1._children] == after)
  167. p1.children *= 1
  168. after = ['a', 'b', 'c']
  169. self.assert_(p1.children == after)
  170. self.assert_([c.name for c in p1._children] == after)
  171. p1.children *= 2
  172. after = ['a', 'b', 'c', 'a', 'b', 'c']
  173. self.assert_(p1.children == after)
  174. self.assert_([c.name for c in p1._children] == after)
  175. p1.children = ['a']
  176. after = ['a']
  177. self.assert_(p1.children == after)
  178. self.assert_([c.name for c in p1._children] == after)
  179. self.assert_((p1.children * 2) == ['a', 'a'])
  180. self.assert_((2 * p1.children) == ['a', 'a'])
  181. self.assert_((p1.children * 0) == [])
  182. self.assert_((0 * p1.children) == [])
  183. self.assert_((p1.children + ['b']) == ['a', 'b'])
  184. self.assert_((['b'] + p1.children) == ['b', 'a'])
  185. try:
  186. p1.children + 123
  187. assert False
  188. except TypeError:
  189. assert True
  190. class DefaultTest(_CollectionOperations):
  191. def __init__(self, *args, **kw):
  192. super(DefaultTest, self).__init__(*args, **kw)
  193. self.collection_class = None
  194. def test_sequence_ops(self):
  195. self._test_sequence_ops()
  196. class ListTest(_CollectionOperations):
  197. def __init__(self, *args, **kw):
  198. super(ListTest, self).__init__(*args, **kw)
  199. self.collection_class = list
  200. def test_sequence_ops(self):
  201. self._test_sequence_ops()
  202. class CustomListTest(ListTest):
  203. def __init__(self, *args, **kw):
  204. super(CustomListTest, self).__init__(*args, **kw)
  205. self.collection_class = list
  206. # No-can-do until ticket #213
  207. class DictTest(_CollectionOperations):
  208. pass
  209. class CustomDictTest(DictTest):
  210. def __init__(self, *args, **kw):
  211. super(DictTest, self).__init__(*args, **kw)
  212. self.collection_class = DictCollection
  213. def test_mapping_ops(self):
  214. Parent, Child = self.Parent, self.Child
  215. p1 = Parent('P1')
  216. self.assert_(not p1._children)
  217. self.assert_(not p1.children)
  218. ch = Child('a', 'regular')
  219. p1._children.append(ch)
  220. self.assert_(ch in p1._children.values())
  221. self.assert_(len(p1._children) == 1)
  222. self.assert_(p1.children)
  223. self.assert_(len(p1.children) == 1)
  224. self.assert_(ch not in p1.children)
  225. self.assert_('a' in p1.children)
  226. self.assert_(p1.children['a'] == 'regular')
  227. self.assert_(p1._children['a'] == ch)
  228. p1.children['b'] = 'proxied'
  229. self.assert_('proxied' in p1.children.values())
  230. self.assert_('b' in p1.children)
  231. self.assert_('proxied' not in p1._children)
  232. self.assert_(len(p1.children) == 2)
  233. self.assert_(len(p1._children) == 2)
  234. self.assert_(p1._children['a'].name == 'regular')
  235. self.assert_(p1._children['b'].name == 'proxied')
  236. del p1._children['b']
  237. self.assert_(len(p1._children) == 1)
  238. self.assert_(len(p1.children) == 1)
  239. self.assert_(p1._children['a'] == ch)
  240. del p1.children['a']
  241. self.assert_(len(p1._children) == 0)
  242. self.assert_(len(p1.children) == 0)
  243. p1.children = {'d': 'v d', 'e': 'v e', 'f': 'v f'}
  244. self.assert_(len(p1._children) == 3)
  245. self.assert_(len(p1.children) == 3)
  246. self.assert_(set(p1.children) == set(['d','e','f']))
  247. del ch
  248. p1 = self.roundtrip(p1)
  249. self.assert_(len(p1._children) == 3)
  250. self.assert_(len(p1.children) == 3)
  251. p1.children['e'] = 'changed-in-place'
  252. self.assert_(p1.children['e'] == 'changed-in-place')
  253. inplace_id = p1._children['e'].id
  254. p1 = self.roundtrip(p1)
  255. self.assert_(p1.children['e'] == 'changed-in-place')
  256. self.assert_(p1._children['e'].id == inplace_id)
  257. p1._children = {}
  258. self.assert_(len(p1.children) == 0)
  259. try:
  260. p1._children = []
  261. self.assert_(False)
  262. except TypeError:
  263. self.assert_(True)
  264. try:
  265. p1._children = None
  266. self.assert_(False)
  267. except TypeError:
  268. self.assert_(True)
  269. assert_raises(TypeError, set, [p1.children])
  270. class SetTest(_CollectionOperations):
  271. def __init__(self, *args, **kw):
  272. super(SetTest, self).__init__(*args, **kw)
  273. self.collection_class = set
  274. def test_set_operations(self):
  275. Parent, Child = self.Parent, self.Child
  276. p1 = Parent('P1')
  277. self.assert_(not p1._children)
  278. self.assert_(not p1.children)
  279. ch1 = Child('regular')
  280. p1._children.add(ch1)
  281. self.assert_(ch1 in p1._children)
  282. self.assert_(len(p1._children) == 1)
  283. self.assert_(p1.children)
  284. self.assert_(len(p1.children) == 1)
  285. self.assert_(ch1 not in p1.children)
  286. self.assert_('regular' in p1.children)
  287. p1.children.add('proxied')
  288. self.assert_('proxied' in p1.children)
  289. self.assert_('proxied' not in p1._children)
  290. self.assert_(len(p1.children) == 2)
  291. self.assert_(len(p1._children) == 2)
  292. self.assert_(set([o.name for o in p1._children]) ==
  293. set(['regular', 'proxied']))
  294. ch2 = None
  295. for o in p1._children:
  296. if o.name == 'proxied':
  297. ch2 = o
  298. break
  299. p1._children.remove(ch2)
  300. self.assert_(len(p1._children) == 1)
  301. self.assert_(len(p1.children) == 1)
  302. self.assert_(p1._children == set([ch1]))
  303. p1.children.remove('regular')
  304. self.assert_(len(p1._children) == 0)
  305. self.assert_(len(p1.children) == 0)
  306. p1.children = ['a','b','c']
  307. self.assert_(len(p1._children) == 3)
  308. self.assert_(len(p1.children) == 3)
  309. del ch1
  310. p1 = self.roundtrip(p1)
  311. self.assert_(len(p1._children) == 3)
  312. self.assert_(len(p1.children) == 3)
  313. self.assert_('a' in p1.children)
  314. self.assert_('b' in p1.children)
  315. self.assert_('d' not in p1.children)
  316. self.assert_(p1.children == set(['a','b','c']))
  317. try:
  318. p1.children.remove('d')
  319. self.fail()
  320. except KeyError:
  321. pass
  322. self.assert_(len(p1.children) == 3)
  323. p1.children.discard('d')
  324. self.assert_(len(p1.children) == 3)
  325. p1 = self.roundtrip(p1)
  326. self.assert_(len(p1.children) == 3)
  327. popped = p1.children.pop()
  328. self.assert_(len(p1.children) == 2)
  329. self.assert_(popped not in p1.children)
  330. p1 = self.roundtrip(p1)
  331. self.assert_(len(p1.children) == 2)
  332. self.assert_(popped not in p1.children)
  333. p1.children = ['a','b','c']
  334. p1 = self.roundtrip(p1)
  335. self.assert_(p1.children == set(['a','b','c']))
  336. p1.children.discard('b')
  337. p1 = self.roundtrip(p1)
  338. self.assert_(p1.children == set(['a', 'c']))
  339. p1.children.remove('a')
  340. p1 = self.roundtrip(p1)
  341. self.assert_(p1.children == set(['c']))
  342. p1._children = set()
  343. self.assert_(len(p1.children) == 0)
  344. try:
  345. p1._children = []
  346. self.assert_(False)
  347. except TypeError:
  348. self.assert_(True)
  349. try:
  350. p1._children = None
  351. self.assert_(False)
  352. except TypeError:
  353. self.assert_(True)
  354. assert_raises(TypeError, set, [p1.children])
  355. def test_set_comparisons(self):
  356. Parent, Child = self.Parent, self.Child
  357. p1 = Parent('P1')
  358. p1.children = ['a','b','c']
  359. control = set(['a','b','c'])
  360. for other in (set(['a','b','c']), set(['a','b','c','d']),
  361. set(['a']), set(['a','b']),
  362. set(['c','d']), set(['e', 'f', 'g']),
  363. set()):
  364. eq_(p1.children.union(other),
  365. control.union(other))
  366. eq_(p1.children.difference(other),
  367. control.difference(other))
  368. eq_((p1.children - other),
  369. (control - other))
  370. eq_(p1.children.intersection(other),
  371. control.intersection(other))
  372. eq_(p1.children.symmetric_difference(other),
  373. control.symmetric_difference(other))
  374. eq_(p1.children.issubset(other),
  375. control.issubset(other))
  376. eq_(p1.children.issuperset(other),
  377. control.issuperset(other))
  378. self.assert_((p1.children == other) == (control == other))
  379. self.assert_((p1.children != other) == (control != other))
  380. self.assert_((p1.children < other) == (control < other))
  381. self.assert_((p1.children <= other) == (control <= other))
  382. self.assert_((p1.children > other) == (control > other))
  383. self.assert_((p1.children >= other) == (control >= other))
  384. def test_set_mutation(self):
  385. Parent, Child = self.Parent, self.Child
  386. # mutations
  387. for op in ('update', 'intersection_update',
  388. 'difference_update', 'symmetric_difference_update'):
  389. for base in (['a', 'b', 'c'], []):
  390. for other in (set(['a','b','c']), set(['a','b','c','d']),
  391. set(['a']), set(['a','b']),
  392. set(['c','d']), set(['e', 'f', 'g']),
  393. set()):
  394. p = Parent('p')
  395. p.children = base[:]
  396. control = set(base[:])
  397. getattr(p.children, op)(other)
  398. getattr(control, op)(other)
  399. try:
  400. self.assert_(p.children == control)
  401. except:
  402. print 'Test %s.%s(%s):' % (set(base), op, other)
  403. print 'want', repr(control)
  404. print 'got', repr(p.children)
  405. raise
  406. p = self.roundtrip(p)
  407. try:
  408. self.assert_(p.children == control)
  409. except:
  410. print 'Test %s.%s(%s):' % (base, op, other)
  411. print 'want', repr(control)
  412. print 'got', repr(p.children)
  413. raise
  414. # in-place mutations
  415. for op in ('|=', '-=', '&=', '^='):
  416. for base in (['a', 'b', 'c'], []):
  417. for other in (set(['a','b','c']), set(['a','b','c','d']),
  418. set(['a']), set(['a','b']),
  419. set(['c','d']), set(['e', 'f', 'g']),
  420. frozenset(['e', 'f', 'g']),
  421. set()):
  422. p = Parent('p')
  423. p.children = base[:]
  424. control = set(base[:])
  425. exec "p.children %s other" % op
  426. exec "control %s other" % op
  427. try:
  428. self.assert_(p.children == control)
  429. except:
  430. print 'Test %s %s %s:' % (set(base), op, other)
  431. print 'want', repr(control)
  432. print 'got', repr(p.children)
  433. raise
  434. p = self.roundtrip(p)
  435. try:
  436. self.assert_(p.children == control)
  437. except:
  438. print 'Test %s %s %s:' % (base, op, other)
  439. print 'want', repr(control)
  440. print 'got', repr(p.children)
  441. raise
  442. class CustomSetTest(SetTest):
  443. def __init__(self, *args, **kw):
  444. super(CustomSetTest, self).__init__(*args, **kw)
  445. self.collection_class = SetCollection
  446. class CustomObjectTest(_CollectionOperations):
  447. def __init__(self, *args, **kw):
  448. super(CustomObjectTest, self).__init__(*args, **kw)
  449. self.collection_class = ObjectCollection
  450. def test_basic(self):
  451. Parent, Child = self.Parent, self.Child
  452. p = Parent('p1')
  453. self.assert_(len(list(p.children)) == 0)
  454. p.children.append('child')
  455. self.assert_(len(list(p.children)) == 1)
  456. p = self.roundtrip(p)
  457. self.assert_(len(list(p.children)) == 1)
  458. # We didn't provide an alternate _AssociationList implementation
  459. # for our ObjectCollection, so indexing will fail.
  460. try:
  461. v = p.children[1]
  462. self.fail()
  463. except TypeError:
  464. pass
  465. class ProxyFactoryTest(ListTest):
  466. def setup(self):
  467. metadata = MetaData(testing.db)
  468. parents_table = Table('Parent', metadata,
  469. Column('id', Integer, primary_key=True,
  470. test_needs_autoincrement=True),
  471. Column('name', String(128)))
  472. children_table = Table('Children', metadata,
  473. Column('id', Integer, primary_key=True,
  474. test_needs_autoincrement=True),
  475. Column('parent_id', Integer,
  476. ForeignKey('Parent.id')),
  477. Column('foo', String(128)),
  478. Column('name', String(128)))
  479. class CustomProxy(_AssociationList):
  480. def __init__(
  481. self,
  482. lazy_collection,
  483. creator,
  484. value_attr,
  485. parent,
  486. ):
  487. getter, setter = parent._default_getset(lazy_collection)
  488. _AssociationList.__init__(
  489. self,
  490. lazy_collection,
  491. creator,
  492. getter,
  493. setter,
  494. parent,
  495. )
  496. class Parent(object):
  497. children = association_proxy('_children', 'name',
  498. proxy_factory=CustomProxy,
  499. proxy_bulk_set=CustomProxy.extend
  500. )
  501. def __init__(self, name):
  502. self.name = name
  503. class Child(object):
  504. def __init__(self, name):
  505. self.name = name
  506. mapper(Parent, parents_table, properties={
  507. '_children': relationship(Child, lazy='joined',
  508. collection_class=list)})
  509. mapper(Child, children_table)
  510. metadata.create_all()
  511. self.metadata = metadata
  512. self.session = create_session()
  513. self.Parent, self.Child = Parent, Child
  514. def test_sequence_ops(self):
  515. self._test_sequence_ops()
  516. class ScalarTest(fixtures.TestBase):
  517. def test_scalar_proxy(self):
  518. metadata = MetaData(testing.db)
  519. parents_table = Table('Parent', metadata,
  520. Column('id', Integer, primary_key=True,
  521. test_needs_autoincrement=True),
  522. Column('name', String(128)))
  523. children_table = Table('Children', metadata,
  524. Column('id', Integer, primary_key=True,
  525. test_needs_autoincrement=True),
  526. Column('parent_id', Integer,
  527. ForeignKey('Parent.id')),
  528. Column('foo', String(128)),
  529. Column('bar', String(128)),
  530. Column('baz', String(128)))
  531. class Parent(object):
  532. foo = association_proxy('child', 'foo')
  533. bar = association_proxy('child', 'bar',
  534. creator=lambda v: Child(bar=v))
  535. baz = association_proxy('child', 'baz',
  536. creator=lambda v: Child(baz=v))
  537. def __init__(self, name):
  538. self.name = name
  539. class Child(object):
  540. def __init__(self, **kw):
  541. for attr in kw:
  542. setattr(self, attr, kw[attr])
  543. mapper(Parent, parents_table, properties={
  544. 'child': relationship(Child, lazy='joined',
  545. backref='parent', uselist=False)})
  546. mapper(Child, children_table)
  547. metadata.create_all()
  548. session = create_session()
  549. def roundtrip(obj):
  550. if obj not in session:
  551. session.add(obj)
  552. session.flush()
  553. id, type_ = obj.id, type(obj)
  554. session.expunge_all()
  555. return session.query(type_).get(id)
  556. p = Parent('p')
  557. # No child
  558. try:
  559. v = p.foo
  560. self.fail()
  561. except:
  562. pass
  563. p.child = Child(foo='a', bar='b', baz='c')
  564. self.assert_(p.foo == 'a')
  565. self.assert_(p.bar == 'b')
  566. self.assert_(p.baz == 'c')
  567. p.bar = 'x'
  568. self.assert_(p.foo == 'a')
  569. self.assert_(p.bar == 'x')
  570. self.assert_(p.baz == 'c')
  571. p = roundtrip(p)
  572. self.assert_(p.foo == 'a')
  573. self.assert_(p.bar == 'x')
  574. self.assert_(p.baz == 'c')
  575. p.child = None
  576. # No child again
  577. try:
  578. v = p.foo
  579. self.fail()
  580. except:
  581. pass
  582. # Bogus creator for this scalar type
  583. try:
  584. p.foo = 'zzz'
  585. self.fail()
  586. except TypeError:
  587. pass
  588. p.bar = 'yyy'
  589. self.assert_(p.foo is None)
  590. self.assert_(p.bar == 'yyy')
  591. self.assert_(p.baz is None)
  592. del p.child
  593. p = roundtrip(p)
  594. self.assert_(p.child is None)
  595. p.baz = 'xxx'
  596. self.assert_(p.foo is None)
  597. self.assert_(p.bar is None)
  598. self.assert_(p.baz == 'xxx')
  599. p = roundtrip(p)
  600. self.assert_(p.foo is None)
  601. self.assert_(p.bar is None)
  602. self.assert_(p.baz == 'xxx')
  603. # Ensure an immediate __set__ works.
  604. p2 = Parent('p2')
  605. p2.bar = 'quux'
  606. class LazyLoadTest(fixtures.TestBase):
  607. def setup(self):
  608. metadata = MetaData(testing.db)
  609. parents_table = Table('Parent', metadata,
  610. Column('id', Integer, primary_key=True,
  611. test_needs_autoincrement=True),
  612. Column('name', String(128)))
  613. children_table = Table('Children', metadata,
  614. Column('id', Integer, primary_key=True,
  615. test_needs_autoincrement=True),
  616. Column('parent_id', Integer,
  617. ForeignKey('Parent.id')),
  618. Column('foo', String(128)),
  619. Column('name', String(128)))
  620. class Parent(object):
  621. children = association_proxy('_children', 'name')
  622. def __init__(self, name):
  623. self.name = name
  624. class Child(object):
  625. def __init__(self, name):
  626. self.name = name
  627. mapper(Child, children_table)
  628. metadata.create_all()
  629. self.metadata = metadata
  630. self.session = create_session()
  631. self.Parent, self.Child = Parent, Child
  632. self.table = parents_table
  633. def teardown(self):
  634. self.metadata.drop_all()
  635. def roundtrip(self, obj):
  636. self.session.add(obj)
  637. self.session.flush()
  638. id, type_ = obj.id, type(obj)
  639. self.session.expunge_all()
  640. return self.session.query(type_).get(id)
  641. def test_lazy_list(self):
  642. Parent, Child = self.Parent, self.Child
  643. mapper(Parent, self.table, properties={
  644. '_children': relationship(Child, lazy='select',
  645. collection_class=list)})
  646. p = Parent('p')
  647. p.children = ['a','b','c']
  648. p = self.roundtrip(p)
  649. # Is there a better way to ensure that the association_proxy
  650. # didn't convert a lazy load to an eager load? This does work though.
  651. self.assert_('_children' not in p.__dict__)
  652. self.assert_(len(p._children) == 3)
  653. self.assert_('_children' in p.__dict__)
  654. def test_eager_list(self):
  655. Parent, Child = self.Parent, self.Child
  656. mapper(Parent, self.table, properties={
  657. '_children': relationship(Child, lazy='joined',
  658. collection_class=list)})
  659. p = Parent('p')
  660. p.children = ['a','b','c']
  661. p = self.roundtrip(p)
  662. self.assert_('_children' in p.__dict__)
  663. self.assert_(len(p._children) == 3)
  664. def test_lazy_scalar(self):
  665. Parent, Child = self.Parent, self.Child
  666. mapper(Parent, self.table, properties={
  667. '_children': relationship(Child, lazy='select', uselist=False)})
  668. p = Parent('p')
  669. p.children = 'value'
  670. p = self.roundtrip(p)
  671. self.assert_('_children' not in p.__dict__)
  672. self.assert_(p._children is not None)
  673. def test_eager_scalar(self):
  674. Parent, Child = self.Parent, self.Child
  675. mapper(Parent, self.table, properties={
  676. '_children': relationship(Child, lazy='joined', uselist=False)})
  677. p = Parent('p')
  678. p.children = 'value'
  679. p = self.roundtrip(p)
  680. self.assert_('_children' in p.__dict__)
  681. self.assert_(p._children is not None)
  682. class Parent(object):
  683. def __init__(self, name):
  684. self.name = name
  685. class Child(object):
  686. def __init__(self, name):
  687. self.name = name
  688. class KVChild(object):
  689. def __init__(self, name, value):
  690. self.name = name
  691. self.value = value
  692. class ReconstitutionTest(fixtures.TestBase):
  693. def setup(self):
  694. metadata = MetaData(testing.db)
  695. parents = Table('parents', metadata, Column('id', Integer,
  696. primary_key=True,
  697. test_needs_autoincrement=True), Column('name',
  698. String(30)))
  699. children = Table('children', metadata, Column('id', Integer,
  700. primary_key=True,
  701. test_needs_autoincrement=True),
  702. Column('parent_id', Integer,
  703. ForeignKey('parents.id')), Column('name',
  704. String(30)))
  705. metadata.create_all()
  706. parents.insert().execute(name='p1')
  707. self.metadata = metadata
  708. self.parents = parents
  709. self.children = children
  710. Parent.kids = association_proxy('children', 'name')
  711. def teardown(self):
  712. self.metadata.drop_all()
  713. clear_mappers()
  714. def test_weak_identity_map(self):
  715. mapper(Parent, self.parents,
  716. properties=dict(children=relationship(Child)))
  717. mapper(Child, self.children)
  718. session = create_session(weak_identity_map=True)
  719. def add_child(parent_name, child_name):
  720. parent = \
  721. session.query(Parent).filter_by(name=parent_name).one()
  722. parent.kids.append(child_name)
  723. add_child('p1', 'c1')
  724. gc_collect()
  725. add_child('p1', 'c2')
  726. session.flush()
  727. p = session.query(Parent).filter_by(name='p1').one()
  728. assert set(p.kids) == set(['c1', 'c2']), p.kids
  729. def test_copy(self):
  730. mapper(Parent, self.parents,
  731. properties=dict(children=relationship(Child)))
  732. mapper(Child, self.children)
  733. p = Parent('p1')
  734. p.kids.extend(['c1', 'c2'])
  735. p_copy = copy.copy(p)
  736. del p
  737. gc_collect()
  738. assert set(p_copy.kids) == set(['c1', 'c2']), p.kids
  739. def test_pickle_list(self):
  740. mapper(Parent, self.parents,
  741. properties=dict(children=relationship(Child)))
  742. mapper(Child, self.children)
  743. p = Parent('p1')
  744. p.kids.extend(['c1', 'c2'])
  745. r1 = pickle.loads(pickle.dumps(p))
  746. assert r1.kids == ['c1', 'c2']
  747. r2 = pickle.loads(pickle.dumps(p.kids))
  748. assert r2 == ['c1', 'c2']
  749. def test_pickle_set(self):
  750. mapper(Parent, self.parents,
  751. properties=dict(children=relationship(Child,
  752. collection_class=set)))
  753. mapper(Child, self.children)
  754. p = Parent('p1')
  755. p.kids.update(['c1', 'c2'])
  756. r1 = pickle.loads(pickle.dumps(p))
  757. assert r1.kids == set(['c1', 'c2'])
  758. r2 = pickle.loads(pickle.dumps(p.kids))
  759. assert r2 == set(['c1', 'c2'])
  760. def test_pickle_dict(self):
  761. mapper(Parent, self.parents,
  762. properties=dict(children=relationship(KVChild,
  763. collection_class=
  764. collections.mapped_collection(PickleKeyFunc('name')))))
  765. mapper(KVChild, self.children)
  766. p = Parent('p1')
  767. p.kids.update({'c1': 'v1', 'c2': 'v2'})
  768. assert p.kids == {'c1': 'c1', 'c2': 'c2'}
  769. r1 = pickle.loads(pickle.dumps(p))
  770. assert r1.kids == {'c1': 'c1', 'c2': 'c2'}
  771. r2 = pickle.loads(pickle.dumps(p.kids))
  772. assert r2 == {'c1': 'c1', 'c2': 'c2'}
  773. class PickleKeyFunc(object):
  774. def __init__(self, name):
  775. self.name = name
  776. def __call__(self, obj):
  777. return getattr(obj, self.name)
  778. class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
  779. __dialect__ = 'default'
  780. run_inserts = 'once'
  781. run_deletes = None
  782. run_setup_mappers = 'once'
  783. run_setup_classes = 'once'
  784. @classmethod
  785. def define_tables(cls, metadata):
  786. Table('userkeywords', metadata,
  787. Column('keyword_id', Integer,ForeignKey('keywords.id'), primary_key=True),
  788. Column('user_id', Integer, ForeignKey('users.id'))
  789. )
  790. Table('users', metadata,
  791. Column('id', Integer,
  792. primary_key=True, test_needs_autoincrement=True),
  793. Column('name', String(64)),
  794. Column('singular_id', Integer, ForeignKey('singular.id'))
  795. )
  796. Table('keywords', metadata,
  797. Column('id', Integer,
  798. primary_key=True, test_needs_autoincrement=True),
  799. Column('keyword', String(64)),
  800. Column('singular_id', Integer, ForeignKey('singular.id'))
  801. )
  802. Table('singular', metadata,
  803. Column('id', Integer,
  804. primary_key=True, test_needs_autoincrement=True),
  805. )
  806. @classmethod
  807. def setup_classes(cls):
  808. class User(cls.Comparable):
  809. def __init__(self, name):
  810. self.name = name
  811. # o2m -> m2o
  812. # uselist -> nonuselist
  813. keywords = association_proxy('user_keywords', 'keyword',
  814. creator=lambda k: UserKeyword(keyword=k))
  815. # m2o -> o2m
  816. # nonuselist -> uselist
  817. singular_keywords = association_proxy('singular', 'keywords')
  818. class Keyword(cls.Comparable):
  819. def __init__(self, keyword):
  820. self.keyword = keyword
  821. # o2o -> m2o
  822. # nonuselist -> nonuselist
  823. user = association_proxy('user_keyword', 'user')
  824. class UserKeyword(cls.Comparable):
  825. def __init__(self, user=None, keyword=None):
  826. self.user = user
  827. self.keyword = keyword
  828. class Singular(cls.Comparable):
  829. def __init__(self, value=None):
  830. self.value = value
  831. @classmethod
  832. def setup_mappers(cls):
  833. users, Keyword, UserKeyword, singular, \
  834. userkeywords, User, keywords, Singular = (cls.tables.users,
  835. cls.classes.Keyword,
  836. cls.classes.UserKeyword,
  837. cls.tables.singular,
  838. cls.tables.userkeywords,
  839. cls.classes.User,
  840. cls.tables.keywords,
  841. cls.classes.Singular)
  842. mapper(User, users, properties={
  843. 'singular':relationship(Singular)
  844. })
  845. mapper(Keyword, keywords, properties={
  846. 'user_keyword':relationship(UserKeyword, uselist=False)
  847. })
  848. mapper(UserKeyword, userkeywords, properties={
  849. 'user' : relationship(User, backref='user_keywords'),
  850. 'keyword' : relationship(Keyword)
  851. })
  852. mapper(Singular, singular, properties={
  853. 'keywords': relationship(Keyword)
  854. })
  855. @classmethod
  856. def insert_data(cls):
  857. UserKeyword, User, Keyword, Singular = (cls.classes.UserKeyword,
  858. cls.classes.User,
  859. cls.classes.Keyword,
  860. cls.classes.Singular)
  861. session = sessionmaker()()
  862. words = (
  863. 'quick', 'brown',
  864. 'fox', 'jumped', 'over',
  865. 'the', 'lazy',
  866. )
  867. for ii in range(4):
  868. user = User('user%d' % ii)
  869. user.singular = Singular()
  870. session.add(user)
  871. for jj in words[ii:ii + 3]:
  872. k = Keyword(jj)
  873. user.keywords.append(k)
  874. user.singular.keywords.append(k)
  875. orphan = Keyword('orphan')
  876. orphan.user_keyword = UserKeyword(keyword=orphan, user=None)
  877. session.add(orphan)
  878. session.commit()
  879. cls.u = user
  880. cls.kw = user.keywords[0]
  881. cls.session = session
  882. def _equivalent(self, q_proxy, q_direct):
  883. eq_(q_proxy.all(), q_direct.all())
  884. def test_filter_any_kwarg_ul_nul(self):
  885. UserKeyword, User = self.classes.UserKeyword, self.classes.User
  886. self._equivalent(self.session.query(User).
  887. filter(User.keywords.any(keyword='jumped'
  888. )),
  889. self.session.query(User).filter(
  890. User.user_keywords.any(
  891. UserKeyword.keyword.has(keyword='jumped'
  892. ))))
  893. def test_filter_has_kwarg_nul_nul(self):
  894. UserKeyword, Keyword = self.classes.UserKeyword, self.classes.Keyword
  895. self._equivalent(self.session.query(Keyword).
  896. filter(Keyword.user.has(name='user2'
  897. )),
  898. self.session.query(Keyword).
  899. filter(Keyword.user_keyword.has(
  900. UserKeyword.user.has(name='user2'
  901. ))))
  902. def test_filter_has_kwarg_nul_ul(self):
  903. User, Singular = self.classes.User, self.classes.Singular
  904. self._equivalent(
  905. self.session.query(User).\
  906. filter(User.singular_keywords.any(keyword='jumped')),
  907. self.session.query(User).\
  908. filter(
  909. User.singular.has(
  910. Singular.keywords.any(keyword='jumped')
  911. )
  912. )
  913. )
  914. def test_filter_any_criterion_ul_nul(self):
  915. UserKeyword, User, Keyword = (self.classes.UserKeyword,
  916. self.classes.User,
  917. self.classes.Keyword)
  918. self._equivalent(self.session.query(User).
  919. filter(User.keywords.any(Keyword.keyword
  920. == 'jumped')),
  921. self.session.query(User).
  922. filter(User.user_keywords.any(
  923. UserKeyword.keyword.has(Keyword.keyword
  924. == 'jumped'))))
  925. def test_filter_has_criterion_nul_nul(self):
  926. UserKeyword, User, Keyword = (self.classes.UserKeyword,
  927. self.classes.User,
  928. self.classes.Keyword)
  929. self._equivalent(self.session.query(Keyword).
  930. filter(Keyword.user.has(User.name
  931. == 'user2')),
  932. self.session.query(Keyword).
  933. filter(Keyword.user_keyword.has(
  934. UserKeyword.user.has(User.name
  935. == 'user2'))))
  936. def test_filter_any_criterion_nul_ul(self):
  937. User, Keyword, Singular = (self.classes.User,
  938. self.classes.Keyword,
  939. self.classes.Singular)
  940. self._equivalent(
  941. self.session.query(User).\
  942. filter(User.singular_keywords.any(Keyword.keyword=='jumped')),
  943. self.session.query(User).\
  944. filter(
  945. User.singular.has(
  946. Singular.keywords.any(Keyword.keyword=='jumped')
  947. )
  948. )
  949. )
  950. def test_filter_contains_ul_nul(self):
  951. User = self.classes.User
  952. self._equivalent(self.session.query(User).
  953. filter(User.keywords.contains(self.kw)),
  954. self.session.query(User).
  955. filter(User.user_keywords.any(keyword=self.kw)))
  956. def test_filter_contains_nul_ul(self):
  957. User, Singular = self.classes.User, self.classes.Singular
  958. self._equivalent(
  959. self.session.query(User).filter(
  960. User.singular_keywords.contains(self.kw)
  961. ),
  962. self.session.query(User).filter(
  963. User.singular.has(
  964. Singular.keywords.contains(self.kw)
  965. )
  966. ),
  967. )
  968. def test_filter_eq_nul_nul(self):
  969. Keyword = self.classes.Keyword
  970. self._equivalent(self.session.query(Keyword).filter(Keyword.user
  971. == self.u),
  972. self.session.query(Keyword).
  973. filter(Keyword.user_keyword.has(user=self.u)))
  974. def test_filter_ne_nul_nul(self):
  975. Keyword = self.classes.Keyword
  976. self._equivalent(self.session.query(Keyword).filter(Keyword.user
  977. != self.u),
  978. self.session.query(Keyword).
  979. filter(not_(Keyword.user_keyword.has(user=self.u))))
  980. def test_filter_eq_null_nul_nul(self):
  981. UserKeyword, Keyword = self.classes.UserKeyword, self.classes.Keyword
  982. self._equivalent(self.session.query(Keyword).filter(Keyword.user
  983. == None),
  984. self.session.query(Keyword).
  985. filter(Keyword.user_keyword.has(UserKeyword.user
  986. == None)))
  987. def test_filter_scalar_contains_fails_nul_nul(self):
  988. Keyword = self.classes.Keyword
  989. assert_raises(exceptions.InvalidRequestError, lambda : \
  990. Keyword.user.contains(self.u))
  991. def test_filter_scalar_any_fails_nul_nul(self):
  992. Keyword = self.classes.Keyword
  993. assert_raises(exceptions.InvalidRequestError, lambda : \
  994. Keyword.user.any(name='user2'))
  995. def test_filter_collection_has_fails_ul_nul(self):
  996. User = self.classes.User
  997. assert_raises(exceptions.InvalidRequestError, lambda : \
  998. User.keywords.has(keyword='quick'))
  999. def test_filter_collection_eq_fails_ul_nul(self):
  1000. User = self.classes.User
  1001. assert_raises(exceptions.InvalidRequestError, lambda : \
  1002. User.keywords == self.kw)
  1003. def test_filter_collection_ne_fails_ul_nul(self):
  1004. User = self.classes.User
  1005. assert_raises(exceptions.InvalidRequestError, lambda : \
  1006. User.keywords != self.kw)
  1007. def test_join_separate_attr(self):
  1008. User = self.classes.User
  1009. self.assert_compile(
  1010. self.session.query(User).join(
  1011. User.keywords.local_attr,
  1012. User.keywords.remote_attr),
  1013. "SELECT users.id AS users_id, users.name AS users_name, "
  1014. "users.singular_id AS users_singular_id "
  1015. "FROM users JOIN userkeywords ON users.id = "
  1016. "userkeywords.user_id JOIN keywords ON keywords.id = "
  1017. "userkeywords.keyword_id"
  1018. )
  1019. def test_join_single_attr(self):
  1020. User = self.classes.User
  1021. self.assert_compile(
  1022. self.session.query(User).join(
  1023. *User.keywords.attr),
  1024. "SELECT users.id AS users_id, users.name AS users_name, "
  1025. "users.singular_id AS users_singular_id "
  1026. "FROM users JOIN userkeywords ON users.id = "
  1027. "userkeywords.user_id JOIN keywords ON keywords.id = "
  1028. "userkeywords.keyword_id"
  1029. )
  1030. class DictOfTupleUpdateTest(fixtures.TestBase):
  1031. def setup(self):
  1032. class B(object):
  1033. def __init__(self, key, elem):
  1034. self.key = key
  1035. self.elem = elem
  1036. class A(object):
  1037. elements = association_proxy("orig", "elem", creator=B)
  1038. m = MetaData()
  1039. a = Table('a', m, Column('id', Integer, primary_key=True))
  1040. b = Table('b', m, Column('id', Integer, primary_key=True),
  1041. Column('aid', Integer, ForeignKey('a.id')))
  1042. mapper(A, a, properties={
  1043. 'orig':relationship(B, collection_class=attribute_mapped_collection('key'))
  1044. })
  1045. mapper(B, b)
  1046. self.A = A
  1047. self.B = B
  1048. def test_update_one_elem_dict(self):
  1049. a1 = self.A()
  1050. a1.elements.update({("B", 3): 'elem2'})
  1051. eq_(a1.elements, {("B",3):'elem2'})
  1052. def test_update_multi_elem_dict(self):
  1053. a1 = self.A()
  1054. a1.elements.update({("B", 3): 'elem2', ("C", 4): "elem3"})
  1055. eq_(a1.elements, {("B",3):'elem2', ("C", 4): "elem3"})
  1056. def test_update_one_elem_list(self):
  1057. a1 = self.A()
  1058. a1.elements.update([(("B", 3), 'elem2')])
  1059. eq_(a1.elements, {("B",3):'elem2'})
  1060. def test_update_multi_elem_list(self):
  1061. a1 = self.A()
  1062. a1.elements.update([(("B", 3), 'elem2'), (("C", 4), "elem3")])
  1063. eq_(a1.elements, {("B",3):'elem2', ("C", 4): "elem3"})
  1064. def test_update_one_elem_varg(self):
  1065. a1 = self.A()
  1066. assert_raises_message(
  1067. ValueError,
  1068. "dictionary update sequence requires "
  1069. "2-element tuples",
  1070. a1.elements.update, (("B", 3), 'elem2')
  1071. )
  1072. def test_update_multi_elem_varg(self):
  1073. a1 = self.A()
  1074. assert_raises_message(
  1075. TypeError,
  1076. "update expected at most 1 arguments, got 2",
  1077. a1.elements.update,
  1078. (("B", 3), 'elem2'), (("C", 4), "elem3")
  1079. )