PageRenderTime 67ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/store/base.py

https://github.com/quodt/storm
Python | 6163 lines | 6028 code | 81 blank | 54 comment | 12 complexity | 7aaf102afe85e3b7417bd8e4385980cb MD5 | raw file
Possible License(s): LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (c) 2006, 2007 Canonical
  4. #
  5. # Written by Gustavo Niemeyer <gustavo@niemeyer.net>
  6. #
  7. # This file is part of Storm Object Relational Mapper.
  8. #
  9. # Storm is free software; you can redistribute it and/or modify
  10. # it under the terms of the GNU Lesser General Public License as
  11. # published by the Free Software Foundation; either version 2.1 of
  12. # the License, or (at your option) any later version.
  13. #
  14. # Storm is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU Lesser General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Lesser General Public License
  20. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. #
  22. from cStringIO import StringIO
  23. import decimal
  24. import gc
  25. import operator
  26. import weakref
  27. from storm.references import Reference, ReferenceSet, Proxy
  28. from storm.database import Result
  29. from storm.properties import Int, Float, RawStr, Unicode, Property, Pickle
  30. from storm.properties import PropertyPublisherMeta, Decimal
  31. from storm.variables import PickleVariable
  32. from storm.expr import (
  33. Asc, Desc, Select, LeftJoin, SQL, Count, Sum, Avg, And, Or, Eq, Lower, Alias)
  34. from storm.variables import Variable, UnicodeVariable, IntVariable
  35. from storm.info import get_obj_info, ClassAlias
  36. from storm.exceptions import (
  37. ClosedError, ConnectionBlockedError, FeatureError, LostObjectError,
  38. NoStoreError, NotFlushedError, NotOneError, OrderLoopError, UnorderedError,
  39. WrongStoreError)
  40. from storm.cache import Cache
  41. from storm.store import AutoReload, EmptyResultSet, Store, ResultSet
  42. from storm.tracer import debug
  43. from tests.info import Wrapper
  44. from tests.helper import TestHelper
  45. class Foo(object):
  46. __storm_table__ = "foo"
  47. id = Int(primary=True)
  48. title = Unicode()
  49. class Bar(object):
  50. __storm_table__ = "bar"
  51. id = Int(primary=True)
  52. title = Unicode()
  53. foo_id = Int()
  54. foo = Reference(foo_id, Foo.id)
  55. class Blob(object):
  56. __storm_table__ = "bin"
  57. id = Int(primary=True)
  58. bin = RawStr()
  59. class Link(object):
  60. __storm_table__ = "link"
  61. __storm_primary__ = "foo_id", "bar_id"
  62. foo_id = Int()
  63. bar_id = Int()
  64. class SelfRef(object):
  65. __storm_table__ = "selfref"
  66. id = Int(primary=True)
  67. title = Unicode()
  68. selfref_id = Int()
  69. selfref = Reference(selfref_id, id)
  70. selfref_on_remote = Reference(id, selfref_id, on_remote=True)
  71. class FooRef(Foo):
  72. bar = Reference(Foo.id, Bar.foo_id)
  73. class FooRefSet(Foo):
  74. bars = ReferenceSet(Foo.id, Bar.foo_id)
  75. class FooRefSetOrderID(Foo):
  76. bars = ReferenceSet(Foo.id, Bar.foo_id, order_by=Bar.id)
  77. class FooRefSetOrderTitle(Foo):
  78. bars = ReferenceSet(Foo.id, Bar.foo_id, order_by=Bar.title)
  79. class FooIndRefSet(Foo):
  80. bars = ReferenceSet(Foo.id, Link.foo_id, Link.bar_id, Bar.id)
  81. class FooIndRefSetOrderID(Foo):
  82. bars = ReferenceSet(Foo.id, Link.foo_id, Link.bar_id, Bar.id,
  83. order_by=Bar.id)
  84. class FooIndRefSetOrderTitle(Foo):
  85. bars = ReferenceSet(Foo.id, Link.foo_id, Link.bar_id, Bar.id,
  86. order_by=Bar.title)
  87. class FooValue(object):
  88. __storm_table__ = "foovalue"
  89. id = Int(primary=True)
  90. foo_id = Int()
  91. value1 = Int()
  92. value2 = Int()
  93. class BarProxy(object):
  94. __storm_table__ = "bar"
  95. id = Int(primary=True)
  96. title = Unicode()
  97. foo_id = Int()
  98. foo = Reference(foo_id, Foo.id)
  99. foo_title = Proxy(foo, Foo.title)
  100. class Money(object):
  101. __storm_table__ = "money"
  102. id = Int(primary=True)
  103. value = Decimal()
  104. class DecorateVariable(Variable):
  105. def parse_get(self, value, to_db):
  106. return u"to_%s(%s)" % (to_db and "db" or "py", value)
  107. def parse_set(self, value, from_db):
  108. return u"from_%s(%s)" % (from_db and "db" or "py", value)
  109. class FooVariable(Foo):
  110. title = Property(variable_class=DecorateVariable)
  111. class DummyDatabase(object):
  112. def connect(self, event=None):
  113. return None
  114. class StoreCacheTest(TestHelper):
  115. def test_wb_custom_cache(self):
  116. cache = Cache(25)
  117. store = Store(DummyDatabase(), cache=cache)
  118. self.assertEquals(store._cache, cache)
  119. def test_wb_default_cache_size(self):
  120. store = Store(DummyDatabase())
  121. self.assertEquals(store._cache._size, 1000)
  122. class StoreDatabaseTest(TestHelper):
  123. def test_store_has_reference_to_its_database(self):
  124. database = DummyDatabase()
  125. store = Store(database)
  126. self.assertIdentical(store.get_database(), database)
  127. class StoreTest(object):
  128. def setUp(self):
  129. self.store = None
  130. self.stores = []
  131. self.create_database()
  132. self.connection = self.database.connect()
  133. self.drop_tables()
  134. self.create_tables()
  135. self.create_sample_data()
  136. self.create_store()
  137. def tearDown(self):
  138. self.drop_store()
  139. self.drop_sample_data()
  140. self.drop_tables()
  141. self.drop_database()
  142. self.connection.close()
  143. def create_database(self):
  144. raise NotImplementedError
  145. def create_tables(self):
  146. raise NotImplementedError
  147. def create_sample_data(self):
  148. connection = self.connection
  149. connection.execute("INSERT INTO foo (id, title)"
  150. " VALUES (10, 'Title 30')")
  151. connection.execute("INSERT INTO foo (id, title)"
  152. " VALUES (20, 'Title 20')")
  153. connection.execute("INSERT INTO foo (id, title)"
  154. " VALUES (30, 'Title 10')")
  155. connection.execute("INSERT INTO bar (id, foo_id, title)"
  156. " VALUES (100, 10, 'Title 300')")
  157. connection.execute("INSERT INTO bar (id, foo_id, title)"
  158. " VALUES (200, 20, 'Title 200')")
  159. connection.execute("INSERT INTO bar (id, foo_id, title)"
  160. " VALUES (300, 30, 'Title 100')")
  161. connection.execute("INSERT INTO bin (id, bin) VALUES (10, 'Blob 30')")
  162. connection.execute("INSERT INTO bin (id, bin) VALUES (20, 'Blob 20')")
  163. connection.execute("INSERT INTO bin (id, bin) VALUES (30, 'Blob 10')")
  164. connection.execute("INSERT INTO link (foo_id, bar_id) VALUES (10, 100)")
  165. connection.execute("INSERT INTO link (foo_id, bar_id) VALUES (10, 200)")
  166. connection.execute("INSERT INTO link (foo_id, bar_id) VALUES (10, 300)")
  167. connection.execute("INSERT INTO link (foo_id, bar_id) VALUES (20, 100)")
  168. connection.execute("INSERT INTO link (foo_id, bar_id) VALUES (20, 200)")
  169. connection.execute("INSERT INTO link (foo_id, bar_id) VALUES (30, 300)")
  170. connection.execute("INSERT INTO money (id, value)"
  171. " VALUES (10, '12.3455')")
  172. connection.execute("INSERT INTO selfref (id, title, selfref_id)"
  173. " VALUES (15, 'SelfRef 15', NULL)")
  174. connection.execute("INSERT INTO selfref (id, title, selfref_id)"
  175. " VALUES (25, 'SelfRef 25', NULL)")
  176. connection.execute("INSERT INTO selfref (id, title, selfref_id)"
  177. " VALUES (35, 'SelfRef 35', 15)")
  178. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  179. " VALUES (1, 10, 2, 1)")
  180. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  181. " VALUES (2, 10, 2, 1)")
  182. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  183. " VALUES (3, 10, 2, 1)")
  184. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  185. " VALUES (4, 10, 2, 2)")
  186. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  187. " VALUES (5, 20, 1, 3)")
  188. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  189. " VALUES (6, 20, 1, 3)")
  190. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  191. " VALUES (7, 20, 1, 4)")
  192. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  193. " VALUES (8, 20, 1, 4)")
  194. connection.execute("INSERT INTO foovalue (id, foo_id, value1, value2)"
  195. " VALUES (9, 20, 1, 2)")
  196. connection.commit()
  197. def create_store(self):
  198. store = Store(self.database)
  199. self.stores.append(store)
  200. if self.store is None:
  201. self.store = store
  202. return store
  203. def drop_store(self):
  204. for store in self.stores:
  205. store.rollback()
  206. # Closing the store is needed because testcase objects are all
  207. # instantiated at once, and thus connections are kept open.
  208. store.close()
  209. def drop_sample_data(self):
  210. pass
  211. def drop_tables(self):
  212. for table in ["foo", "bar", "bin", "link", "money", "selfref",
  213. "foovalue"]:
  214. try:
  215. self.connection.execute("DROP TABLE %s" % table)
  216. self.connection.commit()
  217. except:
  218. self.connection.rollback()
  219. def drop_database(self):
  220. pass
  221. def get_items(self):
  222. # Bypass the store to avoid flushing.
  223. connection = self.store._connection
  224. result = connection.execute("SELECT * FROM foo ORDER BY id")
  225. return list(result)
  226. def get_committed_items(self):
  227. connection = self.database.connect()
  228. result = connection.execute("SELECT * FROM foo ORDER BY id")
  229. return list(result)
  230. def get_cache(self, store):
  231. # We don't offer a public API for this just yet.
  232. return store._cache
  233. def test_execute(self):
  234. result = self.store.execute("SELECT 1")
  235. self.assertTrue(isinstance(result, Result))
  236. self.assertEquals(result.get_one(), (1,))
  237. result = self.store.execute("SELECT 1", noresult=True)
  238. self.assertEquals(result, None)
  239. def test_execute_params(self):
  240. result = self.store.execute("SELECT ?", [1])
  241. self.assertTrue(isinstance(result, Result))
  242. self.assertEquals(result.get_one(), (1,))
  243. def test_execute_flushes(self):
  244. foo = self.store.get(Foo, 10)
  245. foo.title = u"New Title"
  246. result = self.store.execute("SELECT title FROM foo WHERE id=10")
  247. self.assertEquals(result.get_one(), ("New Title",))
  248. def test_close(self):
  249. store = Store(self.database)
  250. store.close()
  251. self.assertRaises(ClosedError, store.execute, "SELECT 1")
  252. def test_get(self):
  253. foo = self.store.get(Foo, 10)
  254. self.assertEquals(foo.id, 10)
  255. self.assertEquals(foo.title, "Title 30")
  256. foo = self.store.get(Foo, 20)
  257. self.assertEquals(foo.id, 20)
  258. self.assertEquals(foo.title, "Title 20")
  259. foo = self.store.get(Foo, 40)
  260. self.assertEquals(foo, None)
  261. def test_get_cached(self):
  262. foo = self.store.get(Foo, 10)
  263. self.assertTrue(self.store.get(Foo, 10) is foo)
  264. def test_wb_get_cached_doesnt_need_connection(self):
  265. foo = self.store.get(Foo, 10)
  266. connection = self.store._connection
  267. self.store._connection = None
  268. self.store.get(Foo, 10)
  269. self.store._connection = connection
  270. def test_cache_cleanup(self):
  271. # Disable the cache, which holds strong references.
  272. self.get_cache(self.store).set_size(0)
  273. foo = self.store.get(Foo, 10)
  274. foo.taint = True
  275. del foo
  276. gc.collect()
  277. foo = self.store.get(Foo, 10)
  278. self.assertFalse(getattr(foo, "taint", False))
  279. def test_add_returns_object(self):
  280. """
  281. Store.add() returns the object passed to it. This allows this
  282. kind of code:
  283. thing = Thing()
  284. store.add(thing)
  285. return thing
  286. to be simplified as:
  287. return store.add(Thing())
  288. """
  289. foo = Foo()
  290. self.assertEquals(self.store.add(foo), foo)
  291. def test_add_and_stop_referencing(self):
  292. # After adding an object, no references should be needed in
  293. # python for it still to be added to the database.
  294. foo = Foo()
  295. foo.title = u"live"
  296. self.store.add(foo)
  297. del foo
  298. gc.collect()
  299. self.assertTrue(self.store.find(Foo, title=u"live").one())
  300. def test_obj_info_with_deleted_object(self):
  301. # Let's try to put Storm in trouble by killing the object
  302. # while still holding a reference to the obj_info.
  303. # Disable the cache, which holds strong references.
  304. self.get_cache(self.store).set_size(0)
  305. class MyFoo(Foo):
  306. loaded = False
  307. def __storm_loaded__(self):
  308. self.loaded = True
  309. foo = self.store.get(MyFoo, 20)
  310. foo.tainted = True
  311. obj_info = get_obj_info(foo)
  312. del foo
  313. gc.collect()
  314. self.assertEquals(obj_info.get_obj(), None)
  315. foo = self.store.find(MyFoo, id=20).one()
  316. self.assertTrue(foo)
  317. self.assertFalse(getattr(foo, "tainted", False))
  318. # The object was rebuilt, so the loaded hook must have run.
  319. self.assertTrue(foo.loaded)
  320. def test_obj_info_with_deleted_object_and_changed_event(self):
  321. """
  322. When an object is collected, the variables disable change notification
  323. to not create a leak. If we're holding a reference to the obj_info and
  324. rebuild the object, it should re-enable change notication.
  325. """
  326. class PickleBlob(Blob):
  327. bin = Pickle()
  328. # Disable the cache, which holds strong references.
  329. self.get_cache(self.store).set_size(0)
  330. blob = self.store.get(Blob, 20)
  331. blob.bin = "\x80\x02}q\x01U\x01aK\x01s."
  332. self.store.flush()
  333. del blob
  334. gc.collect()
  335. pickle_blob = self.store.get(PickleBlob, 20)
  336. obj_info = get_obj_info(pickle_blob)
  337. del pickle_blob
  338. gc.collect()
  339. self.assertEquals(obj_info.get_obj(), None)
  340. pickle_blob = self.store.get(PickleBlob, 20)
  341. pickle_blob.bin = "foobin"
  342. events = []
  343. obj_info.event.hook("changed", lambda *args: events.append(args))
  344. self.store.flush()
  345. self.assertEquals(len(events), 1)
  346. def test_wb_flush_event_with_deleted_object_before_flush(self):
  347. """
  348. When an object is deleted before flush and it contains mutable
  349. variables, those variables unhook from the global event system to
  350. prevent a leak.
  351. """
  352. class PickleBlob(Blob):
  353. bin = Pickle()
  354. # Disable the cache, which holds strong references.
  355. self.get_cache(self.store).set_size(0)
  356. blob = self.store.get(Blob, 20)
  357. blob.bin = "\x80\x02}q\x01U\x01aK\x01s."
  358. self.store.flush()
  359. del blob
  360. gc.collect()
  361. pickle_blob = self.store.get(PickleBlob, 20)
  362. pickle_blob.bin = "foobin"
  363. del pickle_blob
  364. self.store.flush()
  365. self.assertEquals(self.store._event._hooks["flush"], set())
  366. def test_mutable_variable_detect_change_from_alive(self):
  367. """
  368. Changes in a mutable variable like a L{PickleVariable} are correctly
  369. detected, even if the object comes from the alive cache.
  370. """
  371. class PickleBlob(Blob):
  372. bin = Pickle()
  373. blob = PickleBlob()
  374. blob.bin = {"k": "v"}
  375. blob.id = 4000
  376. self.store.add(blob)
  377. self.store.commit()
  378. blob = self.store.find(PickleBlob, PickleBlob.id == 4000).one()
  379. blob.bin["k1"] = "v1"
  380. self.store.commit()
  381. blob = self.store.find(PickleBlob, PickleBlob.id == 4000).one()
  382. self.assertEquals(blob.bin, {"k1": "v1", "k": "v"})
  383. def test_wb_checkpoint_doesnt_override_changed(self):
  384. """
  385. This test ensures that we don't uselessly checkpoint when getting back
  386. objects from the alive cache, which would hide changed values from the
  387. store.
  388. """
  389. foo = self.store.get(Foo, 20)
  390. foo.title = u"changed"
  391. self.store.block_implicit_flushes()
  392. foo2 = self.store.find(Foo, Foo.id == 20).one()
  393. self.store.unblock_implicit_flushes()
  394. self.store.commit()
  395. foo3 = self.store.find(Foo, Foo.id == 20).one()
  396. self.assertEquals(foo3.title, u"changed")
  397. def test_obj_info_with_deleted_object_with_get(self):
  398. # Same thing, but using get rather than find.
  399. # Disable the cache, which holds strong references.
  400. self.get_cache(self.store).set_size(0)
  401. foo = self.store.get(Foo, 20)
  402. foo.tainted = True
  403. obj_info = get_obj_info(foo)
  404. del foo
  405. gc.collect()
  406. self.assertEquals(obj_info.get_obj(), None)
  407. foo = self.store.get(Foo, 20)
  408. self.assertTrue(foo)
  409. self.assertFalse(getattr(foo, "tainted", False))
  410. def test_delete_object_when_obj_info_is_dirty(self):
  411. """Object should stay in memory if dirty."""
  412. # Disable the cache, which holds strong references.
  413. self.get_cache(self.store).set_size(0)
  414. foo = self.store.get(Foo, 20)
  415. foo.title = u"Changed"
  416. foo.tainted = True
  417. obj_info = get_obj_info(foo)
  418. del foo
  419. gc.collect()
  420. self.assertTrue(obj_info.get_obj())
  421. def test_get_tuple(self):
  422. class MyFoo(Foo):
  423. __storm_primary__ = "title", "id"
  424. foo = self.store.get(MyFoo, (u"Title 30", 10))
  425. self.assertEquals(foo.id, 10)
  426. self.assertEquals(foo.title, "Title 30")
  427. foo = self.store.get(MyFoo, (u"Title 20", 10))
  428. self.assertEquals(foo, None)
  429. def test_of(self):
  430. foo = self.store.get(Foo, 10)
  431. self.assertEquals(Store.of(foo), self.store)
  432. self.assertEquals(Store.of(Foo()), None)
  433. self.assertEquals(Store.of(object()), None)
  434. def test_is_empty(self):
  435. result = self.store.find(Foo, id=300)
  436. self.assertEquals(result.is_empty(), True)
  437. result = self.store.find(Foo, id=30)
  438. self.assertEquals(result.is_empty(), False)
  439. def test_is_empty_strips_order_by(self):
  440. """
  441. L{ResultSet.is_empty} strips the C{ORDER BY} clause, if one is
  442. present, since it isn't required to actually determine if a result set
  443. has any matching rows. This should provide a performance improvement
  444. when the ordered result set would be large.
  445. """
  446. stream = StringIO()
  447. self.addCleanup(debug, False)
  448. debug(True, stream)
  449. result = self.store.find(Foo, Foo.id == 300)
  450. result.order_by(Foo.id)
  451. self.assertEqual(True, result.is_empty())
  452. self.assertNotIn("ORDER BY", stream.getvalue())
  453. def test_is_empty_with_composed_key(self):
  454. result = self.store.find(Link, foo_id=300, bar_id=3000)
  455. self.assertEquals(result.is_empty(), True)
  456. result = self.store.find(Link, foo_id=30, bar_id=300)
  457. self.assertEquals(result.is_empty(), False)
  458. def test_is_empty_with_expression_find(self):
  459. result = self.store.find(Foo.title, Foo.id == 300)
  460. self.assertEquals(result.is_empty(), True)
  461. result = self.store.find(Foo.title, Foo.id == 30)
  462. self.assertEquals(result.is_empty(), False)
  463. def test_find_iter(self):
  464. result = self.store.find(Foo)
  465. lst = [(foo.id, foo.title) for foo in result]
  466. lst.sort()
  467. self.assertEquals(lst, [
  468. (10, "Title 30"),
  469. (20, "Title 20"),
  470. (30, "Title 10"),
  471. ])
  472. def test_find_from_cache(self):
  473. foo = self.store.get(Foo, 10)
  474. self.assertTrue(self.store.find(Foo, id=10).one() is foo)
  475. def test_find_expr(self):
  476. result = self.store.find(Foo, Foo.id == 20,
  477. Foo.title == u"Title 20")
  478. self.assertEquals([(foo.id, foo.title) for foo in result], [
  479. (20, "Title 20"),
  480. ])
  481. result = self.store.find(Foo, Foo.id == 10,
  482. Foo.title == u"Title 20")
  483. self.assertEquals([(foo.id, foo.title) for foo in result], [
  484. ])
  485. def test_find_sql(self):
  486. foo = self.store.find(Foo, SQL("foo.id = 20")).one()
  487. self.assertEquals(foo.title, "Title 20")
  488. def test_find_str(self):
  489. foo = self.store.find(Foo, "foo.id = 20").one()
  490. self.assertEquals(foo.title, "Title 20")
  491. def test_find_keywords(self):
  492. result = self.store.find(Foo, id=20, title=u"Title 20")
  493. self.assertEquals([(foo.id, foo.title) for foo in result], [
  494. (20, u"Title 20")
  495. ])
  496. result = self.store.find(Foo, id=10, title=u"Title 20")
  497. self.assertEquals([(foo.id, foo.title) for foo in result], [
  498. ])
  499. def test_find_order_by(self, *args):
  500. result = self.store.find(Foo).order_by(Foo.title)
  501. lst = [(foo.id, foo.title) for foo in result]
  502. self.assertEquals(lst, [
  503. (30, "Title 10"),
  504. (20, "Title 20"),
  505. (10, "Title 30"),
  506. ])
  507. def test_find_order_asc(self, *args):
  508. result = self.store.find(Foo).order_by(Asc(Foo.title))
  509. lst = [(foo.id, foo.title) for foo in result]
  510. self.assertEquals(lst, [
  511. (30, "Title 10"),
  512. (20, "Title 20"),
  513. (10, "Title 30"),
  514. ])
  515. def test_find_order_desc(self, *args):
  516. result = self.store.find(Foo).order_by(Desc(Foo.title))
  517. lst = [(foo.id, foo.title) for foo in result]
  518. self.assertEquals(lst, [
  519. (10, "Title 30"),
  520. (20, "Title 20"),
  521. (30, "Title 10"),
  522. ])
  523. def test_find_default_order_asc(self):
  524. class MyFoo(Foo):
  525. __storm_order__ = "title"
  526. result = self.store.find(MyFoo)
  527. lst = [(foo.id, foo.title) for foo in result]
  528. self.assertEquals(lst, [
  529. (30, "Title 10"),
  530. (20, "Title 20"),
  531. (10, "Title 30"),
  532. ])
  533. def test_find_default_order_desc(self):
  534. class MyFoo(Foo):
  535. __storm_order__ = "-title"
  536. result = self.store.find(MyFoo)
  537. lst = [(foo.id, foo.title) for foo in result]
  538. self.assertEquals(lst, [
  539. (10, "Title 30"),
  540. (20, "Title 20"),
  541. (30, "Title 10"),
  542. ])
  543. def test_find_default_order_with_tuple(self):
  544. class MyLink(Link):
  545. __storm_order__ = ("foo_id", "-bar_id")
  546. result = self.store.find(MyLink)
  547. lst = [(link.foo_id, link.bar_id) for link in result]
  548. self.assertEquals(lst, [
  549. (10, 300),
  550. (10, 200),
  551. (10, 100),
  552. (20, 200),
  553. (20, 100),
  554. (30, 300),
  555. ])
  556. def test_find_default_order_with_tuple_and_expr(self):
  557. class MyLink(Link):
  558. __storm_order__ = ("foo_id", Desc(Link.bar_id))
  559. result = self.store.find(MyLink)
  560. lst = [(link.foo_id, link.bar_id) for link in result]
  561. self.assertEquals(lst, [
  562. (10, 300),
  563. (10, 200),
  564. (10, 100),
  565. (20, 200),
  566. (20, 100),
  567. (30, 300),
  568. ])
  569. def test_find_index(self):
  570. """
  571. L{ResultSet.__getitem__} returns the object at the specified index.
  572. if a slice is used, a new L{ResultSet} is returned configured with the
  573. appropriate offset and limit.
  574. """
  575. foo = self.store.find(Foo).order_by(Foo.title)[0]
  576. self.assertEquals(foo.id, 30)
  577. self.assertEquals(foo.title, "Title 10")
  578. foo = self.store.find(Foo).order_by(Foo.title)[1]
  579. self.assertEquals(foo.id, 20)
  580. self.assertEquals(foo.title, "Title 20")
  581. foo = self.store.find(Foo).order_by(Foo.title)[2]
  582. self.assertEquals(foo.id, 10)
  583. self.assertEquals(foo.title, "Title 30")
  584. foo = self.store.find(Foo).order_by(Foo.title)[1:][1]
  585. self.assertEquals(foo.id, 10)
  586. self.assertEquals(foo.title, "Title 30")
  587. result = self.store.find(Foo).order_by(Foo.title)
  588. self.assertRaises(IndexError, result.__getitem__, 3)
  589. def test_find_slice(self):
  590. result = self.store.find(Foo).order_by(Foo.title)[1:2]
  591. lst = [(foo.id, foo.title) for foo in result]
  592. self.assertEquals(lst,
  593. [(20, "Title 20")])
  594. def test_find_slice_offset(self):
  595. result = self.store.find(Foo).order_by(Foo.title)[1:]
  596. lst = [(foo.id, foo.title) for foo in result]
  597. self.assertEquals(lst,
  598. [(20, "Title 20"),
  599. (10, "Title 30")])
  600. def test_find_slice_offset_any(self):
  601. foo = self.store.find(Foo).order_by(Foo.title)[1:].any()
  602. self.assertEquals(foo.id, 20)
  603. self.assertEquals(foo.title, "Title 20")
  604. def test_find_slice_offset_one(self):
  605. foo = self.store.find(Foo).order_by(Foo.title)[1:2].one()
  606. self.assertEquals(foo.id, 20)
  607. self.assertEquals(foo.title, "Title 20")
  608. def test_find_slice_offset_first(self):
  609. foo = self.store.find(Foo).order_by(Foo.title)[1:].first()
  610. self.assertEquals(foo.id, 20)
  611. self.assertEquals(foo.title, "Title 20")
  612. def test_find_slice_offset_last(self):
  613. foo = self.store.find(Foo).order_by(Foo.title)[1:].last()
  614. self.assertEquals(foo.id, 10)
  615. self.assertEquals(foo.title, "Title 30")
  616. def test_find_slice_limit(self):
  617. result = self.store.find(Foo).order_by(Foo.title)[:2]
  618. lst = [(foo.id, foo.title) for foo in result]
  619. self.assertEquals(lst,
  620. [(30, "Title 10"),
  621. (20, "Title 20")])
  622. def test_find_slice_limit_last(self):
  623. result = self.store.find(Foo).order_by(Foo.title)[:2]
  624. self.assertRaises(FeatureError, result.last)
  625. def test_find_slice_slice(self):
  626. result = self.store.find(Foo).order_by(Foo.title)[0:2][1:3]
  627. lst = [(foo.id, foo.title) for foo in result]
  628. self.assertEquals(lst,
  629. [(20, "Title 20")])
  630. result = self.store.find(Foo).order_by(Foo.title)[:2][1:3]
  631. lst = [(foo.id, foo.title) for foo in result]
  632. self.assertEquals(lst,
  633. [(20, "Title 20")])
  634. result = self.store.find(Foo).order_by(Foo.title)[1:3][0:1]
  635. lst = [(foo.id, foo.title) for foo in result]
  636. self.assertEquals(lst,
  637. [(20, "Title 20")])
  638. result = self.store.find(Foo).order_by(Foo.title)[1:3][:1]
  639. lst = [(foo.id, foo.title) for foo in result]
  640. self.assertEquals(lst,
  641. [(20, "Title 20")])
  642. result = self.store.find(Foo).order_by(Foo.title)[5:5][1:1]
  643. lst = [(foo.id, foo.title) for foo in result]
  644. self.assertEquals(lst, [])
  645. def test_find_slice_order_by(self):
  646. result = self.store.find(Foo)[2:]
  647. self.assertRaises(FeatureError, result.order_by, None)
  648. result = self.store.find(Foo)[:2]
  649. self.assertRaises(FeatureError, result.order_by, None)
  650. def test_find_slice_remove(self):
  651. result = self.store.find(Foo)[2:]
  652. self.assertRaises(FeatureError, result.remove)
  653. result = self.store.find(Foo)[:2]
  654. self.assertRaises(FeatureError, result.remove)
  655. def test_find_contains(self):
  656. foo = self.store.get(Foo, 10)
  657. result = self.store.find(Foo)
  658. self.assertEquals(foo in result, True)
  659. result = self.store.find(Foo, Foo.id == 20)
  660. self.assertEquals(foo in result, False)
  661. result = self.store.find(Foo, "foo.id = 20")
  662. self.assertEquals(foo in result, False)
  663. def test_find_contains_wrong_type(self):
  664. foo = self.store.get(Foo, 10)
  665. bar = self.store.get(Bar, 200)
  666. self.assertRaises(TypeError, operator.contains,
  667. self.store.find(Foo), bar)
  668. self.assertRaises(TypeError, operator.contains,
  669. self.store.find((Foo,)), foo)
  670. self.assertRaises(TypeError, operator.contains,
  671. self.store.find(Foo), (foo,))
  672. self.assertRaises(TypeError, operator.contains,
  673. self.store.find((Foo, Bar)), (bar, foo))
  674. def test_find_contains_does_not_use_iter(self):
  675. def no_iter(self):
  676. raise RuntimeError()
  677. orig_iter = ResultSet.__iter__
  678. ResultSet.__iter__ = no_iter
  679. try:
  680. foo = self.store.get(Foo, 10)
  681. result = self.store.find(Foo)
  682. self.assertEquals(foo in result, True)
  683. finally:
  684. ResultSet.__iter__ = orig_iter
  685. def test_find_contains_with_composed_key(self):
  686. link = self.store.get(Link, (10, 100))
  687. result = self.store.find(Link, Link.foo_id == 10)
  688. self.assertEquals(link in result, True)
  689. result = self.store.find(Link, Link.bar_id == 200)
  690. self.assertEquals(link in result, False)
  691. def test_find_contains_with_set_expression(self):
  692. foo = self.store.get(Foo, 10)
  693. result1 = self.store.find(Foo, Foo.id == 10)
  694. result2 = self.store.find(Foo, Foo.id != 10)
  695. self.assertEquals(foo in result1.union(result2), True)
  696. if self.__class__.__name__.startswith("MySQL"):
  697. return
  698. self.assertEquals(foo in result1.intersection(result2), False)
  699. self.assertEquals(foo in result1.intersection(result1), True)
  700. self.assertEquals(foo in result1.difference(result2), True)
  701. self.assertEquals(foo in result1.difference(result1), False)
  702. def test_find_any(self, *args):
  703. """
  704. L{ResultSet.any} returns an arbitrary objects from the result set.
  705. """
  706. self.assertNotEqual(None, self.store.find(Foo).any())
  707. self.assertEqual(None, self.store.find(Foo, id=40).any())
  708. def test_find_any_strips_order_by(self):
  709. """
  710. L{ResultSet.any} strips the C{ORDER BY} clause, if one is present,
  711. since it isn't required. This should provide a performance
  712. improvement when the ordered result set would be large.
  713. """
  714. stream = StringIO()
  715. self.addCleanup(debug, False)
  716. debug(True, stream)
  717. result = self.store.find(Foo, Foo.id == 300)
  718. result.order_by(Foo.id)
  719. result.any()
  720. self.assertNotIn("ORDER BY", stream.getvalue())
  721. def test_find_first(self, *args):
  722. self.assertRaises(UnorderedError, self.store.find(Foo).first)
  723. foo = self.store.find(Foo).order_by(Foo.title).first()
  724. self.assertEquals(foo.id, 30)
  725. self.assertEquals(foo.title, "Title 10")
  726. foo = self.store.find(Foo).order_by(Foo.id).first()
  727. self.assertEquals(foo.id, 10)
  728. self.assertEquals(foo.title, "Title 30")
  729. foo = self.store.find(Foo, id=40).order_by(Foo.id).first()
  730. self.assertEquals(foo, None)
  731. def test_find_last(self, *args):
  732. self.assertRaises(UnorderedError, self.store.find(Foo).last)
  733. foo = self.store.find(Foo).order_by(Foo.title).last()
  734. self.assertEquals(foo.id, 10)
  735. self.assertEquals(foo.title, "Title 30")
  736. foo = self.store.find(Foo).order_by(Foo.id).last()
  737. self.assertEquals(foo.id, 30)
  738. self.assertEquals(foo.title, "Title 10")
  739. foo = self.store.find(Foo, id=40).order_by(Foo.id).last()
  740. self.assertEquals(foo, None)
  741. def test_find_last_desc(self, *args):
  742. foo = self.store.find(Foo).order_by(Desc(Foo.title)).last()
  743. self.assertEquals(foo.id, 30)
  744. self.assertEquals(foo.title, "Title 10")
  745. foo = self.store.find(Foo).order_by(Asc(Foo.id)).last()
  746. self.assertEquals(foo.id, 30)
  747. self.assertEquals(foo.title, "Title 10")
  748. def test_find_one(self, *args):
  749. self.assertRaises(NotOneError, self.store.find(Foo).one)
  750. foo = self.store.find(Foo, id=10).one()
  751. self.assertEquals(foo.id, 10)
  752. self.assertEquals(foo.title, "Title 30")
  753. foo = self.store.find(Foo, id=40).one()
  754. self.assertEquals(foo, None)
  755. def test_find_count(self):
  756. self.assertEquals(self.store.find(Foo).count(), 3)
  757. def test_find_count_after_slice(self):
  758. """
  759. When we slice a ResultSet obtained after a set operation (like union),
  760. we get a fresh select that doesn't modify the limit and offset
  761. attribute of the original ResultSet.
  762. """
  763. result1 = self.store.find(Foo, Foo.id == 10)
  764. result2 = self.store.find(Foo, Foo.id == 20)
  765. result3 = result1.union(result2)
  766. result3.order_by(Foo.id)
  767. self.assertEquals(result3.count(), 2)
  768. result_slice = list(result3[:2])
  769. self.assertEquals(result3.count(), 2)
  770. def test_find_count_column(self):
  771. self.assertEquals(self.store.find(Link).count(Link.foo_id), 6)
  772. def test_find_count_column_distinct(self):
  773. count = self.store.find(Link).count(Link.foo_id, distinct=True)
  774. self.assertEquals(count, 3)
  775. def test_find_limit_count(self):
  776. result = self.store.find(Link.foo_id)
  777. result.config(limit=2)
  778. count = result.count()
  779. self.assertEquals(count, 2)
  780. def test_find_offset_count(self):
  781. result = self.store.find(Link.foo_id)
  782. result.config(offset=3)
  783. count = result.count()
  784. self.assertEquals(count, 3)
  785. def test_find_sliced_count(self):
  786. result = self.store.find(Link.foo_id)
  787. count = result[2:4].count()
  788. self.assertEquals(count, 2)
  789. def test_find_distinct_count(self):
  790. result = self.store.find(Link.foo_id)
  791. result.config(distinct=True)
  792. count = result.count()
  793. self.assertEquals(count, 3)
  794. def test_find_distinct_order_by_limit_count(self):
  795. result = self.store.find(Foo)
  796. result.order_by(Foo.title)
  797. result.config(distinct=True, limit=3)
  798. count = result.count()
  799. self.assertEquals(count, 3)
  800. def test_find_distinct_count_multiple_columns(self):
  801. result = self.store.find((Link.foo_id, Link.bar_id))
  802. result.config(distinct=True)
  803. count = result.count()
  804. self.assertEquals(count, 6)
  805. def test_find_count_column_with_implicit_distinct(self):
  806. result = self.store.find(Link)
  807. result.config(distinct=True)
  808. count = result.count(Link.foo_id)
  809. self.assertEquals(count, 6)
  810. def test_find_max(self):
  811. self.assertEquals(self.store.find(Foo).max(Foo.id), 30)
  812. def test_find_max_expr(self):
  813. self.assertEquals(self.store.find(Foo).max(Foo.id + 1), 31)
  814. def test_find_max_unicode(self):
  815. title = self.store.find(Foo).max(Foo.title)
  816. self.assertEquals(title, "Title 30")
  817. self.assertTrue(isinstance(title, unicode))
  818. def test_find_max_with_empty_result_and_disallow_none(self):
  819. class Bar(object):
  820. __storm_table__ = "bar"
  821. id = Int(primary=True)
  822. foo_id = Int(allow_none=False)
  823. result = self.store.find(Bar, Bar.id > 1000)
  824. self.assertTrue(result.is_empty())
  825. self.assertEquals(result.max(Bar.foo_id), None)
  826. def test_find_min(self):
  827. self.assertEquals(self.store.find(Foo).min(Foo.id), 10)
  828. def test_find_min_expr(self):
  829. self.assertEquals(self.store.find(Foo).min(Foo.id - 1), 9)
  830. def test_find_min_unicode(self):
  831. title = self.store.find(Foo).min(Foo.title)
  832. self.assertEquals(title, "Title 10")
  833. self.assertTrue(isinstance(title, unicode))
  834. def test_find_min_with_empty_result_and_disallow_none(self):
  835. class Bar(object):
  836. __storm_table__ = "bar"
  837. id = Int(primary=True)
  838. foo_id = Int(allow_none=False)
  839. result = self.store.find(Bar, Bar.id > 1000)
  840. self.assertTrue(result.is_empty())
  841. self.assertEquals(result.min(Bar.foo_id), None)
  842. def test_find_avg(self):
  843. self.assertEquals(self.store.find(Foo).avg(Foo.id), 20)
  844. def test_find_avg_expr(self):
  845. self.assertEquals(self.store.find(Foo).avg(Foo.id + 10), 30)
  846. def test_find_avg_float(self):
  847. foo = Foo()
  848. foo.id = 15
  849. foo.title = u"Title 15"
  850. self.store.add(foo)
  851. self.assertEquals(self.store.find(Foo).avg(Foo.id), 18.75)
  852. def test_find_sum(self):
  853. self.assertEquals(self.store.find(Foo).sum(Foo.id), 60)
  854. def test_find_sum_expr(self):
  855. self.assertEquals(self.store.find(Foo).sum(Foo.id * 2), 120)
  856. def test_find_sum_with_empty_result_and_disallow_none(self):
  857. class Bar(object):
  858. __storm_table__ = "bar"
  859. id = Int(primary=True)
  860. foo_id = Int(allow_none=False)
  861. result = self.store.find(Bar, Bar.id > 1000)
  862. self.assertTrue(result.is_empty())
  863. self.assertEquals(result.sum(Bar.foo_id), None)
  864. def test_find_max_order_by(self):
  865. """Interaction between order by and aggregation shouldn't break."""
  866. result = self.store.find(Foo)
  867. self.assertEquals(result.order_by(Foo.id).max(Foo.id), 30)
  868. def test_find_get_select_expr_without_columns(self):
  869. """
  870. A L{FeatureError} is raised if L{ResultSet.get_select_expr} is called
  871. without a list of L{Column}s.
  872. """
  873. result = self.store.find(Foo)
  874. self.assertRaises(FeatureError, result.get_select_expr)
  875. def test_find_get_select_expr(self):
  876. """
  877. Only the specified L{Column}s are included in the L{Select} expression
  878. provided by L{ResultSet.get_select_expr}.
  879. """
  880. foo = self.store.get(Foo, 10)
  881. result1 = self.store.find(Foo, Foo.id <= 10)
  882. subselect = result1.get_select_expr(Foo.id)
  883. self.assertEqual((Foo.id,), subselect.columns)
  884. result2 = self.store.find(Foo, Foo.id.is_in(subselect))
  885. self.assertEqual([foo], list(result2))
  886. def test_find_get_select_expr_with_set_expression(self):
  887. """
  888. A L{FeatureError} is raised if L{ResultSet.get_select_expr} is used
  889. with a L{ResultSet} that represents a set expression, such as a union.
  890. """
  891. result1 = self.store.find(Foo, Foo.id == 10)
  892. result2 = self.store.find(Foo, Foo.id == 20)
  893. result3 = result1.union(result2)
  894. self.assertRaises(FeatureError, result3.get_select_expr, Foo.id)
  895. def test_find_values(self):
  896. values = self.store.find(Foo).order_by(Foo.id).values(Foo.id)
  897. self.assertEquals(list(values), [10, 20, 30])
  898. values = self.store.find(Foo).order_by(Foo.id).values(Foo.title)
  899. values = list(values)
  900. self.assertEquals(values, ["Title 30", "Title 20", "Title 10"])
  901. self.assertEquals([type(value) for value in values],
  902. [unicode, unicode, unicode])
  903. def test_find_multiple_values(self):
  904. result = self.store.find(Foo).order_by(Foo.id)
  905. values = result.values(Foo.id, Foo.title)
  906. self.assertEquals(list(values),
  907. [(10, "Title 30"),
  908. (20, "Title 20"),
  909. (30, "Title 10")])
  910. def test_find_values_with_no_arguments(self):
  911. result = self.store.find(Foo).order_by(Foo.id)
  912. self.assertRaises(FeatureError, result.values().next)
  913. def test_find_slice_values(self):
  914. values = self.store.find(Foo).order_by(Foo.id)[1:2].values(Foo.id)
  915. self.assertEquals(list(values), [20])
  916. def test_find_values_with_set_expression(self):
  917. """
  918. A L{FeatureError} is raised if L{ResultSet.values} is used with a
  919. L{ResultSet} that represents a set expression, such as a union.
  920. """
  921. result1 = self.store.find(Foo, Foo.id == 10)
  922. result2 = self.store.find(Foo, Foo.id == 20)
  923. result3 = result1.union(result2)
  924. self.assertRaises(FeatureError, list, result3.values(Foo.id))
  925. def test_find_remove(self):
  926. self.store.find(Foo, Foo.id == 20).remove()
  927. self.assertEquals(self.get_items(), [
  928. (10, "Title 30"),
  929. (30, "Title 10"),
  930. ])
  931. def test_find_cached(self):
  932. foo = self.store.get(Foo, 20)
  933. bar = self.store.get(Bar, 200)
  934. self.assertTrue(foo)
  935. self.assertTrue(bar)
  936. self.assertEquals(self.store.find(Foo).cached(), [foo])
  937. def test_find_cached_where(self):
  938. foo1 = self.store.get(Foo, 10)
  939. foo2 = self.store.get(Foo, 20)
  940. bar = self.store.get(Bar, 200)
  941. self.assertTrue(foo1)
  942. self.assertTrue(foo2)
  943. self.assertTrue(bar)
  944. self.assertEquals(self.store.find(Foo, title=u"Title 20").cached(),
  945. [foo2])
  946. def test_find_cached_invalidated(self):
  947. foo = self.store.get(Foo, 20)
  948. self.store.invalidate(foo)
  949. self.assertEquals(self.store.find(Foo).cached(), [foo])
  950. def test_find_cached_invalidated_and_deleted(self):
  951. foo = self.store.get(Foo, 20)
  952. self.store.execute("DELETE FROM foo WHERE id=20")
  953. self.store.invalidate(foo)
  954. # Do not look for the primary key (id), since it's able to get
  955. # it without touching the database. Use the title instead.
  956. self.assertEquals(self.store.find(Foo, title=u"Title 20").cached(), [])
  957. def test_find_cached_with_info_alive_and_object_dead(self):
  958. # Disable the cache, which holds strong references.
  959. self.get_cache(self.store).set_size(0)
  960. foo = self.store.get(Foo, 20)
  961. foo.tainted = True
  962. obj_info = get_obj_info(foo)
  963. del foo
  964. gc.collect()
  965. cached = self.store.find(Foo).cached()
  966. self.assertEquals(len(cached), 1)
  967. foo = self.store.get(Foo, 20)
  968. self.assertFalse(hasattr(foo, "tainted"))
  969. def test_using_find_join(self):
  970. bar = self.store.get(Bar, 100)
  971. bar.foo_id = None
  972. tables = self.store.using(Foo, LeftJoin(Bar, Bar.foo_id == Foo.id))
  973. result = tables.find(Bar).order_by(Foo.id, Bar.id)
  974. lst = [bar and (bar.id, bar.title) for bar in result]
  975. self.assertEquals(lst, [
  976. None,
  977. (200, u"Title 200"),
  978. (300, u"Title 100"),
  979. ])
  980. def test_using_find_with_strings(self):
  981. foo = self.store.using("foo").find(Foo, id=10).one()
  982. self.assertEquals(foo.title, "Title 30")
  983. foo = self.store.using("foo", "bar").find(Foo, id=10).any()
  984. self.assertEquals(foo.title, "Title 30")
  985. def test_using_find_join_with_strings(self):
  986. bar = self.store.get(Bar, 100)
  987. bar.foo_id = None
  988. tables = self.store.using(LeftJoin("foo", "bar",
  989. "bar.foo_id = foo.id"))
  990. result = tables.find(Bar).order_by(Foo.id, Bar.id)
  991. lst = [bar and (bar.id, bar.title) for bar in result]
  992. self.assertEquals(lst, [
  993. None,
  994. (200, u"Title 200"),
  995. (300, u"Title 100"),
  996. ])
  997. def test_find_tuple(self):
  998. bar = self.store.get(Bar, 200)
  999. bar.foo_id = None
  1000. result = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1001. result = result.order_by(Foo.id)
  1002. lst = [(foo and (foo.id, foo.title), bar and (bar.id, bar.title))
  1003. for (foo, bar) in result]
  1004. self.assertEquals(lst, [
  1005. ((10, u"Title 30"), (100, u"Title 300")),
  1006. ((30, u"Title 10"), (300, u"Title 100")),
  1007. ])
  1008. def test_find_tuple_using(self):
  1009. bar = self.store.get(Bar, 200)
  1010. bar.foo_id = None
  1011. tables = self.store.using(Foo, LeftJoin(Bar, Bar.foo_id == Foo.id))
  1012. result = tables.find((Foo, Bar)).order_by(Foo.id)
  1013. lst = [(foo and (foo.id, foo.title), bar and (bar.id, bar.title))
  1014. for (foo, bar) in result]
  1015. self.assertEquals(lst, [
  1016. ((10, u"Title 30"), (100, u"Title 300")),
  1017. ((20, u"Title 20"), None),
  1018. ((30, u"Title 10"), (300, u"Title 100")),
  1019. ])
  1020. def test_find_tuple_using_with_disallow_none(self):
  1021. class Bar(object):
  1022. __storm_table__ = "bar"
  1023. id = Int(primary=True, allow_none=False)
  1024. title = Unicode()
  1025. foo_id = Int()
  1026. foo = Reference(foo_id, Foo.id)
  1027. bar = self.store.get(Bar, 200)
  1028. self.store.remove(bar)
  1029. tables = self.store.using(Foo, LeftJoin(Bar, Bar.foo_id == Foo.id))
  1030. result = tables.find((Foo, Bar)).order_by(Foo.id)
  1031. lst = [(foo and (foo.id, foo.title), bar and (bar.id, bar.title))
  1032. for (foo, bar) in result]
  1033. self.assertEquals(lst, [
  1034. ((10, u"Title 30"), (100, u"Title 300")),
  1035. ((20, u"Title 20"), None),
  1036. ((30, u"Title 10"), (300, u"Title 100")),
  1037. ])
  1038. def test_find_tuple_using_skip_when_none(self):
  1039. bar = self.store.get(Bar, 200)
  1040. bar.foo_id = None
  1041. tables = self.store.using(Foo,
  1042. LeftJoin(Bar, Bar.foo_id == Foo.id),
  1043. LeftJoin(Link, Link.bar_id == Bar.id))
  1044. result = tables.find((Bar, Link)).order_by(Foo.id, Bar.id, Link.foo_id)
  1045. lst = [(bar and (bar.id, bar.title),
  1046. link and (link.bar_id, link.foo_id))
  1047. for (bar, link) in result]
  1048. self.assertEquals(lst, [
  1049. ((100, u"Title 300"), (100, 10)),
  1050. ((100, u"Title 300"), (100, 20)),
  1051. (None, None),
  1052. ((300, u"Title 100"), (300, 10)),
  1053. ((300, u"Title 100"), (300, 30)),
  1054. ])
  1055. def test_find_tuple_contains(self):
  1056. foo = self.store.get(Foo, 10)
  1057. bar = self.store.get(Bar, 100)
  1058. bar200 = self.store.get(Bar, 200)
  1059. result = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1060. self.assertEquals((foo, bar) in result, True)
  1061. self.assertEquals((foo, bar200) in result, False)
  1062. def test_find_tuple_contains_with_set_expression(self):
  1063. foo = self.store.get(Foo, 10)
  1064. bar = self.store.get(Bar, 100)
  1065. bar200 = self.store.get(Bar, 200)
  1066. result1 = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1067. result2 = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1068. self.assertEquals((foo, bar) in result1.union(result2), True)
  1069. if self.__class__.__name__.startswith("MySQL"):
  1070. return
  1071. self.assertEquals((foo, bar) in result1.intersection(result2), True)
  1072. self.assertEquals((foo, bar) in result1.difference(result2), False)
  1073. def test_find_tuple_any(self):
  1074. bar = self.store.get(Bar, 200)
  1075. bar.foo_id = None
  1076. result = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1077. foo, bar = result.order_by(Foo.id).any()
  1078. self.assertEquals(foo.id, 10)
  1079. self.assertEquals(foo.title, u"Title 30")
  1080. self.assertEquals(bar.id, 100)
  1081. self.assertEquals(bar.title, u"Title 300")
  1082. def test_find_tuple_first(self):
  1083. bar = self.store.get(Bar, 200)
  1084. bar.foo_id = None
  1085. result = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1086. foo, bar = result.order_by(Foo.id).first()
  1087. self.assertEquals(foo.id, 10)
  1088. self.assertEquals(foo.title, u"Title 30")
  1089. self.assertEquals(bar.id, 100)
  1090. self.assertEquals(bar.title, u"Title 300")
  1091. def test_find_tuple_last(self):
  1092. bar = self.store.get(Bar, 200)
  1093. bar.foo_id = None
  1094. result = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1095. foo, bar = result.order_by(Foo.id).last()
  1096. self.assertEquals(foo.id, 30)
  1097. self.assertEquals(foo.title, u"Title 10")
  1098. self.assertEquals(bar.id, 300)
  1099. self.assertEquals(bar.title, u"Title 100")
  1100. def test_find_tuple_one(self):
  1101. bar = self.store.get(Bar, 200)
  1102. bar.foo_id = None
  1103. result = self.store.find((Foo, Bar),
  1104. Bar.foo_id == Foo.id, Foo.id == 10)
  1105. foo, bar = result.order_by(Foo.id).one()
  1106. self.assertEquals(foo.id, 10)
  1107. self.assertEquals(foo.title, u"Title 30")
  1108. self.assertEquals(bar.id, 100)
  1109. self.assertEquals(bar.title, u"Title 300")
  1110. def test_find_tuple_count(self):
  1111. bar = self.store.get(Bar, 200)
  1112. bar.foo_id = None
  1113. result = self.store.find((Foo, Bar), Bar.foo_id == Foo.id)
  1114. self.assertEquals(result.count(), 2)
  1115. def test_find_tuple_remove(self):
  1116. result = self.store.find((Foo, Bar))
  1117. self.assertRaises(FeatureError, result.remove)
  1118. def test_find_tuple_set(self):
  1119. result = self.store.find((Foo, Bar))
  1120. self.assertRaises(FeatureError, result.set, title=u"Title 40")
  1121. def test_find_tuple_kwargs(self):
  1122. self.assertRaises(FeatureError,
  1123. self.store.find, (Foo, Bar), title=u"Titl…

Large files files are truncated, but you can click here to view the full file