PageRenderTime 33ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/gluon/tests/test_dal_nosql.py

https://github.com/eddgt/web2py
Python | 1432 lines | 1416 code | 5 blank | 11 comment | 6 complexity | 590c70f2ae532e209b4fbf335d97fd7f MD5 | raw file
Possible License(s): BSD-2-Clause, AGPL-3.0, LGPL-3.0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Unit tests for gluon.dal (NoSQL adapters)
  5. """
  6. import sys
  7. import os
  8. import glob
  9. if sys.version < "2.7":
  10. import unittest2 as unittest
  11. else:
  12. import unittest
  13. import datetime
  14. try:
  15. import cStringIO as StringIO
  16. except:
  17. from io import StringIO
  18. def fix_sys_path():
  19. """
  20. logic to have always the correct sys.path
  21. '', web2py/gluon, web2py/site-packages, web2py/ ...
  22. """
  23. def add_path_first(path):
  24. sys.path = [path] + [p for p in sys.path if (
  25. not p == path and not p == (path + '/'))]
  26. path = os.path.dirname(os.path.abspath(__file__))
  27. if not os.path.isfile(os.path.join(path,'web2py.py')):
  28. i = 0
  29. while i<10:
  30. i += 1
  31. if os.path.exists(os.path.join(path,'web2py.py')):
  32. break
  33. path = os.path.abspath(os.path.join(path, '..'))
  34. paths = [path,
  35. os.path.abspath(os.path.join(path, 'site-packages')),
  36. os.path.abspath(os.path.join(path, 'gluon')),
  37. '']
  38. [add_path_first(path) for path in paths]
  39. fix_sys_path()
  40. #for travis-ci
  41. DEFAULT_URI = os.environ.get('DB', 'sqlite:memory')
  42. print 'Testing against %s engine (%s)' % (DEFAULT_URI.partition(':')[0], DEFAULT_URI)
  43. IS_GAE = "datastore" in DEFAULT_URI
  44. IS_MONGODB = "mongodb" in DEFAULT_URI
  45. IS_IMAP = "imap" in DEFAULT_URI
  46. if IS_IMAP:
  47. from dal import IMAPAdapter
  48. from contrib import mockimaplib
  49. IMAPAdapter.driver = mockimaplib
  50. from dal import DAL, Field, Table, SQLALL
  51. def drop(table, cascade=None):
  52. # mongodb implements drop()
  53. # although it seems it does not work properly
  54. if (IS_GAE or IS_MONGODB or IS_IMAP):
  55. # GAE drop/cleanup is not implemented
  56. db = table._db
  57. db(table).delete()
  58. del db[table._tablename]
  59. del db.tables[db.tables.index(table._tablename)]
  60. db._remove_references_to(table)
  61. else:
  62. if cascade:
  63. table.drop(cascade)
  64. else:
  65. table.drop()
  66. # setup GAE dummy database
  67. if IS_GAE:
  68. from google.appengine.ext import testbed
  69. gaetestbed = testbed.Testbed()
  70. gaetestbed.activate()
  71. gaetestbed.init_datastore_v3_stub()
  72. ALLOWED_DATATYPES = [
  73. 'string',
  74. 'text',
  75. 'integer',
  76. 'boolean',
  77. 'double',
  78. 'blob',
  79. 'date',
  80. 'time',
  81. 'datetime',
  82. 'upload',
  83. 'password',
  84. 'json',
  85. ]
  86. def setUpModule():
  87. pass
  88. def tearDownModule():
  89. if os.path.isfile('sql.log'):
  90. os.unlink('sql.log')
  91. for a in glob.glob('*.table'):
  92. os.unlink(a)
  93. @unittest.skipIf(IS_GAE or IS_IMAP, 'TODO: Datastore throws "AssertionError: SyntaxError not raised"')
  94. class TestFields(unittest.TestCase):
  95. def testFieldName(self):
  96. # Check that Fields cannot start with underscores
  97. self.assertRaises(SyntaxError, Field, '_abc', 'string')
  98. # Check that Fields cannot contain punctuation other than underscores
  99. self.assertRaises(SyntaxError, Field, 'a.bc', 'string')
  100. # Check that Fields cannot be a name of a method or property of Table
  101. for x in ['drop', 'on', 'truncate']:
  102. self.assertRaises(SyntaxError, Field, x, 'string')
  103. # Check that Fields allows underscores in the body of a field name.
  104. self.assert_(Field('a_bc', 'string'),
  105. "Field isn't allowing underscores in fieldnames. It should.")
  106. def testFieldTypes(self):
  107. # Check that string, and password default length is 512
  108. for typ in ['string', 'password']:
  109. self.assert_(Field('abc', typ).length == 512,
  110. "Default length for type '%s' is not 512 or 255" % typ)
  111. # Check that upload default length is 512
  112. self.assert_(Field('abc', 'upload').length == 512,
  113. "Default length for type 'upload' is not 512")
  114. # Check that Tables passed in the type creates a reference
  115. self.assert_(Field('abc', Table(None, 'temp')).type
  116. == 'reference temp',
  117. 'Passing an Table does not result in a reference type.')
  118. def testFieldLabels(self):
  119. # Check that a label is successfully built from the supplied fieldname
  120. self.assert_(Field('abc', 'string').label == 'Abc',
  121. 'Label built is incorrect')
  122. self.assert_(Field('abc_def', 'string').label == 'Abc Def',
  123. 'Label built is incorrect')
  124. def testFieldFormatters(self): # Formatter should be called Validator
  125. # Test the default formatters
  126. for typ in ALLOWED_DATATYPES:
  127. f = Field('abc', typ)
  128. if typ not in ['date', 'time', 'datetime']:
  129. isinstance(f.formatter('test'), str)
  130. else:
  131. isinstance(f.formatter(datetime.datetime.now()), str)
  132. @unittest.skipIf(IS_GAE or IS_MONGODB, 'TODO: Datastore does accept dict objects as json field input. MongoDB assertion error Binary("x", 0) != "x"')
  133. def testRun(self):
  134. db = DAL(DEFAULT_URI, check_reserved=['all'])
  135. for ft in ['string', 'text', 'password', 'upload', 'blob']:
  136. db.define_table('tt', Field('aa', ft, default=''))
  137. self.assertEqual(isinstance(db.tt.insert(aa='x'), long), True)
  138. self.assertEqual(db().select(db.tt.aa)[0].aa, 'x')
  139. drop(db.tt)
  140. db.define_table('tt', Field('aa', 'integer', default=1))
  141. self.assertEqual(isinstance(db.tt.insert(aa=3), long), True)
  142. self.assertEqual(db().select(db.tt.aa)[0].aa, 3)
  143. drop(db.tt)
  144. db.define_table('tt', Field('aa', 'double', default=1))
  145. self.assertEqual(isinstance(db.tt.insert(aa=3.1), long), True)
  146. self.assertEqual(db().select(db.tt.aa)[0].aa, 3.1)
  147. drop(db.tt)
  148. db.define_table('tt', Field('aa', 'boolean', default=True))
  149. self.assertEqual(isinstance(db.tt.insert(aa=True), long), True)
  150. self.assertEqual(db().select(db.tt.aa)[0].aa, True)
  151. drop(db.tt)
  152. db.define_table('tt', Field('aa', 'json', default={}))
  153. self.assertEqual(isinstance(db.tt.insert(aa={}), long), True)
  154. self.assertEqual(db().select(db.tt.aa)[0].aa, {})
  155. drop(db.tt)
  156. db.define_table('tt', Field('aa', 'date',
  157. default=datetime.date.today()))
  158. t0 = datetime.date.today()
  159. self.assertEqual(isinstance(db.tt.insert(aa=t0), long), True)
  160. self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
  161. drop(db.tt)
  162. db.define_table('tt', Field('aa', 'datetime',
  163. default=datetime.datetime.today()))
  164. t0 = datetime.datetime(
  165. 1971,
  166. 12,
  167. 21,
  168. 10,
  169. 30,
  170. 55,
  171. 0,
  172. )
  173. self.assertEqual(isinstance(db.tt.insert(aa=t0), long), True)
  174. self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
  175. ## Row APIs
  176. row = db().select(db.tt.aa)[0]
  177. self.assertEqual(db.tt[1].aa,t0)
  178. self.assertEqual(db.tt['aa'],db.tt.aa)
  179. self.assertEqual(db.tt(1).aa,t0)
  180. self.assertTrue(db.tt(1,aa=None)==None)
  181. self.assertFalse(db.tt(1,aa=t0)==None)
  182. self.assertEqual(row.aa,t0)
  183. self.assertEqual(row['aa'],t0)
  184. self.assertEqual(row['tt.aa'],t0)
  185. self.assertEqual(row('tt.aa'),t0)
  186. ## Lazy and Virtual fields
  187. db.tt.b = Field.Virtual(lambda row: row.tt.aa)
  188. db.tt.c = Field.Lazy(lambda row: row.tt.aa)
  189. row = db().select(db.tt.aa)[0]
  190. self.assertEqual(row.b,t0)
  191. self.assertEqual(row.c(),t0)
  192. drop(db.tt)
  193. db.define_table('tt', Field('aa', 'time', default='11:30'))
  194. t0 = datetime.time(10, 30, 55)
  195. self.assertEqual(isinstance(db.tt.insert(aa=t0), long), True)
  196. self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
  197. drop(db.tt)
  198. @unittest.skipIf(IS_GAE or IS_IMAP, 'TODO: Datastore throws "AssertionError: SyntaxError not raised"')
  199. class TestTables(unittest.TestCase):
  200. def testTableNames(self):
  201. # Check that Tables cannot start with underscores
  202. self.assertRaises(SyntaxError, Table, None, '_abc')
  203. # Check that Tables cannot contain punctuation other than underscores
  204. self.assertRaises(SyntaxError, Table, None, 'a.bc')
  205. # Check that Tables cannot be a name of a method or property of DAL
  206. for x in ['define_table', 'tables', 'as_dict']:
  207. self.assertRaises(SyntaxError, Table, None, x)
  208. # Check that Table allows underscores in the body of a field name.
  209. self.assert_(Table(None, 'a_bc'),
  210. "Table isn't allowing underscores in tablename. It should.")
  211. @unittest.skipIf(IS_IMAP, "Skip IMAP")
  212. class TestAll(unittest.TestCase):
  213. def setUp(self):
  214. self.pt = Table(None,'PseudoTable',Field('name'),Field('birthdate'))
  215. def testSQLALL(self):
  216. ans = 'PseudoTable.id, PseudoTable.name, PseudoTable.birthdate'
  217. self.assertEqual(str(SQLALL(self.pt)), ans)
  218. @unittest.skipIf(IS_IMAP, "Skip IMAP")
  219. class TestTable(unittest.TestCase):
  220. def testTableCreation(self):
  221. # Check for error when not passing type other than Field or Table
  222. self.assertRaises(SyntaxError, Table, None, 'test', None)
  223. persons = Table(None, 'persons',
  224. Field('firstname','string'),
  225. Field('lastname', 'string'))
  226. # Does it have the correct fields?
  227. self.assert_(set(persons.fields).issuperset(set(['firstname',
  228. 'lastname'])))
  229. # ALL is set correctly
  230. self.assert_('persons.firstname, persons.lastname'
  231. in str(persons.ALL))
  232. @unittest.skipIf(IS_GAE or IS_MONGODB, "No table alias for this backend")
  233. def testTableAlias(self):
  234. db = DAL(DEFAULT_URI, check_reserved=['all'])
  235. persons = Table(db, 'persons', Field('firstname',
  236. 'string'), Field('lastname', 'string'))
  237. aliens = persons.with_alias('aliens')
  238. # Are the different table instances with the same fields
  239. self.assert_(persons is not aliens)
  240. self.assert_(set(persons.fields) == set(aliens.fields))
  241. def testTableInheritance(self):
  242. persons = Table(None, 'persons', Field('firstname',
  243. 'string'), Field('lastname', 'string'))
  244. customers = Table(None, 'customers',
  245. Field('items_purchased', 'integer'),
  246. persons)
  247. self.assert_(set(customers.fields).issuperset(set(
  248. ['items_purchased', 'firstname', 'lastname'])))
  249. class TestInsert(unittest.TestCase):
  250. def testRun(self):
  251. if IS_IMAP:
  252. imap = DAL(DEFAULT_URI)
  253. imap.define_tables()
  254. self.assertEqual(imap.Draft.insert(to="nurse@example.com",
  255. subject="Nurse!",
  256. sender="gumby@example.com",
  257. content="Nurse!\r\nNurse!"), 2)
  258. self.assertEqual(imap.Draft[2].subject, "Nurse!")
  259. self.assertEqual(imap.Draft[2].sender, "gumby@example.com")
  260. self.assertEqual(isinstance(imap.Draft[2].uid, long), True)
  261. self.assertEqual(imap.Draft[2].content[0]["text"], "Nurse!\r\nNurse!")
  262. else:
  263. db = DAL(DEFAULT_URI, check_reserved=['all'])
  264. db.define_table('tt', Field('aa'))
  265. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  266. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  267. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  268. self.assertEqual(db(db.tt.aa == '1').count(), 3)
  269. self.assertEqual(db(db.tt.aa == '2').isempty(), True)
  270. self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3)
  271. self.assertEqual(db(db.tt.aa == '2').count(), 3)
  272. self.assertEqual(db(db.tt.aa == '2').isempty(), False)
  273. self.assertEqual(db(db.tt.aa == '2').delete(), 3)
  274. self.assertEqual(db(db.tt.aa == '2').isempty(), True)
  275. drop(db.tt)
  276. @unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, 'TODO: Datastore throws "SyntaxError: Not supported (query using or)". MongoDB assertionerror 5L != 3')
  277. class TestSelect(unittest.TestCase):
  278. def testRun(self):
  279. db = DAL(DEFAULT_URI, check_reserved=['all'])
  280. db.define_table('tt', Field('aa'))
  281. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  282. self.assertEqual(isinstance(db.tt.insert(aa='2'), long), True)
  283. self.assertEqual(isinstance(db.tt.insert(aa='3'), long), True)
  284. self.assertEqual(db(db.tt.id > 0).count(), 3)
  285. self.assertEqual(db(db.tt.id > 0).select(orderby=~db.tt.aa
  286. | db.tt.id)[0].aa, '3')
  287. self.assertEqual(len(db(db.tt.id > 0).select(limitby=(1, 2))), 1)
  288. self.assertEqual(db(db.tt.id > 0).select(limitby=(1, 2))[0].aa,
  289. '2')
  290. self.assertEqual(len(db().select(db.tt.ALL)), 3)
  291. self.assertEqual(db(db.tt.aa == None).count(), 0)
  292. self.assertEqual(db(db.tt.aa != None).count(), 3)
  293. self.assertEqual(db(db.tt.aa > '1').count(), 2)
  294. self.assertEqual(db(db.tt.aa >= '1').count(), 3)
  295. self.assertEqual(db(db.tt.aa == '1').count(), 1)
  296. self.assertEqual(db(db.tt.aa != '1').count(), 2)
  297. self.assertEqual(db(db.tt.aa < '3').count(), 2)
  298. self.assertEqual(db(db.tt.aa <= '3').count(), 3)
  299. self.assertEqual(db(db.tt.aa > '1')(db.tt.aa < '3').count(), 1)
  300. self.assertEqual(db((db.tt.aa > '1') & (db.tt.aa < '3')).count(), 1)
  301. self.assertEqual(db((db.tt.aa > '1') | (db.tt.aa < '3')).count(), 3)
  302. self.assertEqual(db((db.tt.aa > '1') & ~(db.tt.aa > '2')).count(), 1)
  303. self.assertEqual(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).count(), 0)
  304. drop(db.tt)
  305. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  306. class TestAddMethod(unittest.TestCase):
  307. def testRun(self):
  308. db = DAL(DEFAULT_URI, check_reserved=['all'])
  309. db.define_table('tt', Field('aa'))
  310. @db.tt.add_method.all
  311. def select_all(table,orderby=None):
  312. return table._db(table).select(orderby=orderby)
  313. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  314. self.assertEqual(isinstance(db.tt.insert(aa='2'), long), True)
  315. self.assertEqual(isinstance(db.tt.insert(aa='3'), long), True)
  316. self.assertEqual(len(db.tt.all()), 3)
  317. drop(db.tt)
  318. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  319. class TestBelongs(unittest.TestCase):
  320. def testRun(self):
  321. db = DAL(DEFAULT_URI, check_reserved=['all'])
  322. db.define_table('tt', Field('aa'))
  323. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  324. self.assertEqual(isinstance(db.tt.insert(aa='2'), long), True)
  325. self.assertEqual(isinstance(db.tt.insert(aa='3'), long), True)
  326. self.assertEqual(db(db.tt.aa.belongs(('1', '3'))).count(),
  327. 2)
  328. if not (IS_GAE or IS_MONGODB):
  329. self.assertEqual(db(db.tt.aa.belongs(db(db.tt.id > 2)._select(db.tt.aa))).count(), 1)
  330. self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1',
  331. '3')))._select(db.tt.aa))).count(), 2)
  332. self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(db
  333. (db.tt.aa.belongs(('1', '3')))._select(db.tt.aa)))._select(
  334. db.tt.aa))).count(),
  335. 2)
  336. else:
  337. print "Datastore/Mongodb belongs does not accept queries (skipping)"
  338. drop(db.tt)
  339. @unittest.skipIf(IS_GAE or IS_IMAP, "Contains not supported on GAE Datastore. TODO: IMAP tests")
  340. class TestContains(unittest.TestCase):
  341. @unittest.skipIf(IS_MONGODB, "TODO: MongoDB Contains error")
  342. def testRun(self):
  343. db = DAL(DEFAULT_URI, check_reserved=['all'])
  344. db.define_table('tt', Field('aa', 'list:string'), Field('bb','string'))
  345. self.assertEqual(isinstance(db.tt.insert(aa=['aaa','bbb'],bb='aaa'), long), True)
  346. self.assertEqual(isinstance(db.tt.insert(aa=['bbb','ddd'],bb='abb'), long), True)
  347. self.assertEqual(isinstance(db.tt.insert(aa=['eee','aaa'],bb='acc'), long), True)
  348. self.assertEqual(db(db.tt.aa.contains('aaa')).count(), 2)
  349. self.assertEqual(db(db.tt.aa.contains('bbb')).count(), 2)
  350. self.assertEqual(db(db.tt.aa.contains('aa')).count(), 0)
  351. self.assertEqual(db(db.tt.bb.contains('a')).count(), 3)
  352. self.assertEqual(db(db.tt.bb.contains('b')).count(), 1)
  353. self.assertEqual(db(db.tt.bb.contains('d')).count(), 0)
  354. self.assertEqual(db(db.tt.aa.contains(db.tt.bb)).count(), 1)
  355. drop(db.tt)
  356. @unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, "Like not supported on GAE Datastore. TODO: IMAP test")
  357. class TestLike(unittest.TestCase):
  358. def testRun(self):
  359. db = DAL(DEFAULT_URI, check_reserved=['all'])
  360. db.define_table('tt', Field('aa'))
  361. self.assertEqual(isinstance(db.tt.insert(aa='abc'), long), True)
  362. self.assertEqual(db(db.tt.aa.like('a%')).count(), 1)
  363. self.assertEqual(db(db.tt.aa.like('%b%')).count(), 1)
  364. self.assertEqual(db(db.tt.aa.like('%c')).count(), 1)
  365. self.assertEqual(db(db.tt.aa.like('%d%')).count(), 0)
  366. self.assertEqual(db(db.tt.aa.lower().like('A%')).count(), 1)
  367. self.assertEqual(db(db.tt.aa.lower().like('%B%')).count(),
  368. 1)
  369. self.assertEqual(db(db.tt.aa.lower().like('%C')).count(), 1)
  370. self.assertEqual(db(db.tt.aa.upper().like('A%')).count(), 1)
  371. self.assertEqual(db(db.tt.aa.upper().like('%B%')).count(),
  372. 1)
  373. self.assertEqual(db(db.tt.aa.upper().like('%C')).count(), 1)
  374. drop(db.tt)
  375. db.define_table('tt', Field('aa', 'integer'))
  376. self.assertEqual(isinstance(db.tt.insert(aa=1111111111), long), True)
  377. self.assertEqual(db(db.tt.aa.like('1%')).count(), 1)
  378. self.assertEqual(db(db.tt.aa.like('2%')).count(), 0)
  379. drop(db.tt)
  380. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  381. class TestDatetime(unittest.TestCase):
  382. def testRun(self):
  383. db = DAL(DEFAULT_URI, check_reserved=['all'])
  384. db.define_table('tt', Field('aa', 'datetime'))
  385. self.assertEqual(isinstance(db.tt.insert(aa=datetime.datetime(1971, 12, 21,
  386. 11, 30)), long), True)
  387. self.assertEqual(isinstance(db.tt.insert(aa=datetime.datetime(1971, 11, 21,
  388. 10, 30)), long), True)
  389. self.assertEqual(isinstance(db.tt.insert(aa=datetime.datetime(1970, 12, 21,
  390. 9, 30)), long), True)
  391. self.assertEqual(db(db.tt.aa == datetime.datetime(1971, 12,
  392. 21, 11, 30)).count(), 1)
  393. self.assertEqual(db(db.tt.aa >= datetime.datetime(1971, 1, 1)).count(), 2)
  394. drop(db.tt)
  395. @unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, "Expressions are not supported")
  396. class TestExpressions(unittest.TestCase):
  397. def testRun(self):
  398. db = DAL(DEFAULT_URI, check_reserved=['all'])
  399. db.define_table('tt', Field('aa', 'integer'))
  400. self.assertEqual(isinstance(db.tt.insert(aa=1), long), True)
  401. self.assertEqual(isinstance(db.tt.insert(aa=2), long), True)
  402. self.assertEqual(isinstance(db.tt.insert(aa=3), long), True)
  403. self.assertEqual(db(db.tt.aa == 3).update(aa=db.tt.aa + 1), 1)
  404. self.assertEqual(db(db.tt.aa == 4).count(), 1)
  405. self.assertEqual(db(db.tt.aa == -2).count(), 0)
  406. sum = (db.tt.aa + 1).sum()
  407. self.assertEqual(db(db.tt.aa == 2).select(sum).first()[sum], 3)
  408. self.assertEqual(db(db.tt.aa == -2).select(sum).first()[sum], None)
  409. drop(db.tt)
  410. @unittest.skip("JOIN queries are not supported")
  411. class TestJoin(unittest.TestCase):
  412. def testRun(self):
  413. db = DAL(DEFAULT_URI, check_reserved=['all'])
  414. db.define_table('t1', Field('aa'))
  415. db.define_table('t2', Field('aa'), Field('b', db.t1))
  416. i1 = db.t1.insert(aa='1')
  417. i2 = db.t1.insert(aa='2')
  418. i3 = db.t1.insert(aa='3')
  419. db.t2.insert(aa='4', b=i1)
  420. db.t2.insert(aa='5', b=i2)
  421. db.t2.insert(aa='6', b=i2)
  422. self.assertEqual(len(db(db.t1.id
  423. == db.t2.b).select(orderby=db.t1.aa
  424. | db.t2.aa)), 3)
  425. self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
  426. | db.t2.aa)[2].t1.aa, '2')
  427. self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
  428. | db.t2.aa)[2].t2.aa, '6')
  429. self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL,
  430. left=db.t2.on(db.t1.id == db.t2.b),
  431. orderby=db.t1.aa | db.t2.aa)), 4)
  432. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  433. left=db.t2.on(db.t1.id == db.t2.b),
  434. orderby=db.t1.aa | db.t2.aa)[2].t1.aa, '2')
  435. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  436. left=db.t2.on(db.t1.id == db.t2.b),
  437. orderby=db.t1.aa | db.t2.aa)[2].t2.aa, '6')
  438. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  439. left=db.t2.on(db.t1.id == db.t2.b),
  440. orderby=db.t1.aa | db.t2.aa)[3].t1.aa, '3')
  441. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  442. left=db.t2.on(db.t1.id == db.t2.b),
  443. orderby=db.t1.aa | db.t2.aa)[3].t2.aa, None)
  444. self.assertEqual(len(db().select(db.t1.aa, db.t2.id.count(),
  445. left=db.t2.on(db.t1.id == db.t2.b),
  446. orderby=db.t1.aa, groupby=db.t1.aa)),
  447. 3)
  448. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  449. left=db.t2.on(db.t1.id == db.t2.b),
  450. orderby=db.t1.aa,
  451. groupby=db.t1.aa)[0]._extra[db.t2.id.count()],
  452. 1)
  453. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  454. left=db.t2.on(db.t1.id == db.t2.b),
  455. orderby=db.t1.aa,
  456. groupby=db.t1.aa)[1]._extra[db.t2.id.count()],
  457. 2)
  458. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  459. left=db.t2.on(db.t1.id == db.t2.b),
  460. orderby=db.t1.aa,
  461. groupby=db.t1.aa)[2]._extra[db.t2.id.count()],
  462. 0)
  463. drop(db.t2)
  464. drop(db.t1)
  465. db.define_table('person',Field('name'))
  466. id = db.person.insert(name="max")
  467. self.assertEqual(id.name,'max')
  468. db.define_table('dog',Field('name'),Field('ownerperson','reference person'))
  469. db.dog.insert(name='skipper',ownerperson=1)
  470. row = db(db.person.id==db.dog.ownerperson).select().first()
  471. self.assertEqual(row[db.person.name],'max')
  472. self.assertEqual(row['person.name'],'max')
  473. drop(db.dog)
  474. self.assertEqual(len(db.person._referenced_by),0)
  475. drop(db.person)
  476. class TestMinMaxSumAvg(unittest.TestCase):
  477. @unittest.skipIf(IS_GAE or IS_MONGODB or IS_IMAP, 'TODO: Datastore throws "AttributeError: Row object has no attribute _extra"')
  478. def testRun(self):
  479. db = DAL(DEFAULT_URI, check_reserved=['all'])
  480. db.define_table('tt', Field('aa', 'integer'))
  481. self.assertEqual(isinstance(db.tt.insert(aa=1), long), True)
  482. self.assertEqual(isinstance(db.tt.insert(aa=2), long), True)
  483. self.assertEqual(isinstance(db.tt.insert(aa=3), long), True)
  484. s = db.tt.aa.min()
  485. self.assertEqual(db(db.tt.id > 0).select(s)[0]._extra[s], 1)
  486. self.assertEqual(db(db.tt.id > 0).select(s).first()[s], 1)
  487. self.assertEqual(db().select(s).first()[s], 1)
  488. s = db.tt.aa.max()
  489. self.assertEqual(db().select(s).first()[s], 3)
  490. s = db.tt.aa.sum()
  491. self.assertEqual(db().select(s).first()[s], 6)
  492. s = db.tt.aa.count()
  493. self.assertEqual(db().select(s).first()[s], 3)
  494. s = db.tt.aa.avg()
  495. self.assertEqual(db().select(s).first()[s], 2)
  496. drop(db.tt)
  497. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  498. class TestCache(unittest.TestCase):
  499. def testRun(self):
  500. from cache import CacheInRam
  501. cache = CacheInRam()
  502. db = DAL(DEFAULT_URI, check_reserved=['all'])
  503. db.define_table('tt', Field('aa'))
  504. db.tt.insert(aa='1')
  505. r0 = db().select(db.tt.ALL)
  506. r1 = db().select(db.tt.ALL, cache=(cache, 1000))
  507. self.assertEqual(len(r0),len(r1))
  508. r2 = db().select(db.tt.ALL, cache=(cache, 1000))
  509. self.assertEqual(len(r0),len(r2))
  510. r3 = db().select(db.tt.ALL, cache=(cache, 1000), cacheable=True)
  511. self.assertEqual(len(r0),len(r3))
  512. r4 = db().select(db.tt.ALL, cache=(cache, 1000), cacheable=True)
  513. self.assertEqual(len(r0),len(r4))
  514. drop(db.tt)
  515. @unittest.skipIf(IS_IMAP, "Skip IMAP")
  516. class TestMigrations(unittest.TestCase):
  517. def testRun(self):
  518. db = DAL(DEFAULT_URI, check_reserved=['all'])
  519. db.define_table('tt', Field('aa'), migrate='.storage.table')
  520. db.commit()
  521. db.close()
  522. db = DAL(DEFAULT_URI, check_reserved=['all'])
  523. db.define_table('tt', Field('aa'), Field('b'),
  524. migrate='.storage.table')
  525. db.commit()
  526. db.close()
  527. db = DAL(DEFAULT_URI, check_reserved=['all'])
  528. db.define_table('tt', Field('aa'), Field('b', 'text'),
  529. migrate='.storage.table')
  530. db.commit()
  531. db.close()
  532. db = DAL(DEFAULT_URI, check_reserved=['all'])
  533. db.define_table('tt', Field('aa'), migrate='.storage.table')
  534. drop(db.tt)
  535. db.commit()
  536. db.close()
  537. def tearDown(self):
  538. if os.path.exists('.storage.db'):
  539. os.unlink('.storage.db')
  540. if os.path.exists('.storage.table'):
  541. os.unlink('.storage.table')
  542. class TestReference(unittest.TestCase):
  543. @unittest.skipIf(IS_MONGODB or IS_IMAP, "TODO: MongoDB assertion error (long object has no attribute id)")
  544. def testRun(self):
  545. db = DAL(DEFAULT_URI, check_reserved=['all'])
  546. if DEFAULT_URI.startswith('mssql'):
  547. #multiple cascade gotcha
  548. for key in ['reference','reference FK']:
  549. db._adapter.types[key]=db._adapter.types[key].replace(
  550. '%(on_delete_action)s','NO ACTION')
  551. db.define_table('tt', Field('name'), Field('aa','reference tt'))
  552. db.commit()
  553. x = db.tt.insert(name='max')
  554. assert isinstance(x.id, long) == True
  555. assert isinstance(x['id'], long) == True
  556. x.aa = x
  557. assert isinstance(x.aa, long) == True
  558. x.update_record()
  559. y = db.tt[x.id]
  560. assert y.aa == x.aa
  561. assert y.aa.aa.aa.aa.aa.aa.name == 'max'
  562. z=db.tt.insert(name='xxx', aa = y)
  563. assert z.aa == y.id
  564. drop(db.tt)
  565. db.commit()
  566. @unittest.skipIf(IS_IMAP, "Skip IMAP")
  567. class TestClientLevelOps(unittest.TestCase):
  568. def testRun(self):
  569. db = DAL(DEFAULT_URI, check_reserved=['all'])
  570. db.define_table('tt', Field('aa'))
  571. db.commit()
  572. db.tt.insert(aa="test")
  573. rows1 = db(db.tt.aa=='test').select()
  574. rows2 = db(db.tt.aa=='test').select()
  575. rows3 = rows1 & rows2
  576. assert len(rows3) == 2
  577. rows4 = rows1 | rows2
  578. assert len(rows4) == 1
  579. rows5 = rows1.find(lambda row: row.aa=="test")
  580. assert len(rows5) == 1
  581. rows6 = rows2.exclude(lambda row: row.aa=="test")
  582. assert len(rows6) == 1
  583. rows7 = rows5.sort(lambda row: row.aa)
  584. assert len(rows7) == 1
  585. drop(db.tt)
  586. db.commit()
  587. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  588. class TestVirtualFields(unittest.TestCase):
  589. def testRun(self):
  590. db = DAL(DEFAULT_URI, check_reserved=['all'])
  591. db.define_table('tt', Field('aa'))
  592. db.commit()
  593. db.tt.insert(aa="test")
  594. class Compute:
  595. def a_upper(row): return row.tt.aa.upper()
  596. db.tt.virtualfields.append(Compute())
  597. assert db(db.tt.id>0).select().first().a_upper == 'TEST'
  598. drop(db.tt)
  599. db.commit()
  600. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  601. class TestComputedFields(unittest.TestCase):
  602. def testRun(self):
  603. db = DAL(DEFAULT_URI, check_reserved=['all'])
  604. db.define_table('tt',
  605. Field('aa'),
  606. Field('bb',default='x'),
  607. Field('cc',compute=lambda r: r.aa+r.bb))
  608. db.commit()
  609. id = db.tt.insert(aa="z")
  610. self.assertEqual(db.tt[id].cc,'zx')
  611. drop(db.tt)
  612. db.commit()
  613. # test checking that a compute field can refer to earlier-defined computed fields
  614. db.define_table('tt',
  615. Field('aa'),
  616. Field('bb',default='x'),
  617. Field('cc',compute=lambda r: r.aa+r.bb),
  618. Field('dd',compute=lambda r: r.bb + r.cc))
  619. db.commit()
  620. id = db.tt.insert(aa="z")
  621. self.assertEqual(db.tt[id].dd,'xzx')
  622. drop(db.tt)
  623. db.commit()
  624. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  625. class TestCommonFilters(unittest.TestCase):
  626. @unittest.skipIf(IS_MONGODB, "TODO: MongoDB Assertion error")
  627. def testRun(self):
  628. db = DAL(DEFAULT_URI, check_reserved=['all'])
  629. db.define_table('t1', Field('aa'))
  630. # db.define_table('t2', Field('aa'), Field('b', db.t1))
  631. i1 = db.t1.insert(aa='1')
  632. i2 = db.t1.insert(aa='2')
  633. i3 = db.t1.insert(aa='3')
  634. # db.t2.insert(aa='4', b=i1)
  635. # db.t2.insert(aa='5', b=i2)
  636. # db.t2.insert(aa='6', b=i2)
  637. db.t1._common_filter = lambda q: db.t1.aa>'1'
  638. self.assertEqual(db(db.t1).count(),2)
  639. # self.assertEqual(db(db.t1).count(),2)
  640. # q = db.t2.b==db.t1.id
  641. # q = db.t1.aa != None
  642. # self.assertEqual(db(q).count(),2)
  643. # self.assertEqual(db(q).count(),2)
  644. # self.assertEqual(len(db(db.t1).select(left=db.t2.on(q))),3)
  645. # db.t2._common_filter = lambda q: db.t2.aa<6
  646. # self.assertEqual(db(q).count(),1)
  647. # self.assertEqual(db(q).count(),1)
  648. # self.assertEqual(len(db(db.t1).select(left=db.t2.on(q))),2)
  649. # drop(db.t2)
  650. drop(db.t1)
  651. @unittest.skipIf(IS_IMAP, "Skip IMAP test")
  652. class TestImportExportFields(unittest.TestCase):
  653. def testRun(self):
  654. db = DAL(DEFAULT_URI, check_reserved=['all'])
  655. db.define_table('person', Field('name'))
  656. db.define_table('pet',Field('friend',db.person),Field('name'))
  657. for n in range(2):
  658. db(db.pet).delete()
  659. db(db.person).delete()
  660. for k in range(10):
  661. id = db.person.insert(name=str(k))
  662. db.pet.insert(friend=id,name=str(k))
  663. db.commit()
  664. stream = StringIO.StringIO()
  665. db.export_to_csv_file(stream)
  666. db(db.pet).delete()
  667. db(db.person).delete()
  668. stream = StringIO.StringIO(stream.getvalue())
  669. db.import_from_csv_file(stream)
  670. assert db(db.person).count()==10
  671. assert db(db.pet.name).count()==10
  672. drop(db.pet)
  673. drop(db.person)
  674. db.commit()
  675. @unittest.skipIf(IS_IMAP, "Skip IMAP test")
  676. class TestImportExportUuidFields(unittest.TestCase):
  677. def testRun(self):
  678. db = DAL(DEFAULT_URI, check_reserved=['all'])
  679. db.define_table('person', Field('name'),Field('uuid'))
  680. db.define_table('pet',Field('friend',db.person),Field('name'))
  681. for n in range(2):
  682. db(db.pet).delete()
  683. db(db.person).delete()
  684. for k in range(10):
  685. id = db.person.insert(name=str(k),uuid=str(k))
  686. db.pet.insert(friend=id,name=str(k))
  687. db.commit()
  688. stream = StringIO.StringIO()
  689. db.export_to_csv_file(stream)
  690. db(db.person).delete()
  691. db(db.pet).delete()
  692. stream = StringIO.StringIO(stream.getvalue())
  693. db.import_from_csv_file(stream)
  694. assert db(db.person).count()==10
  695. assert db(db.pet).count()==10
  696. drop(db.pet)
  697. drop(db.person)
  698. db.commit()
  699. @unittest.skipIf(IS_IMAP, "Skip IMAP test")
  700. class TestDALDictImportExport(unittest.TestCase):
  701. def testRun(self):
  702. db = DAL(DEFAULT_URI, check_reserved=['all'])
  703. db.define_table('person', Field('name', default="Michael"),Field('uuid'))
  704. db.define_table('pet',Field('friend',db.person),Field('name'))
  705. dbdict = db.as_dict(flat=True, sanitize=False)
  706. assert isinstance(dbdict, dict)
  707. uri = dbdict["uri"]
  708. assert isinstance(uri, basestring) and uri
  709. assert len(dbdict["tables"]) == 2
  710. assert len(dbdict["tables"][0]["fields"]) == 3
  711. assert dbdict["tables"][0]["fields"][1]["type"] == db.person.name.type
  712. assert dbdict["tables"][0]["fields"][1]["default"] == db.person.name.default
  713. db2 = DAL(**dbdict)
  714. assert len(db.tables) == len(db2.tables)
  715. assert hasattr(db2, "pet") and isinstance(db2.pet, Table)
  716. assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field)
  717. drop(db.pet)
  718. db.commit()
  719. db2.commit()
  720. have_serializers = True
  721. try:
  722. import serializers
  723. dbjson = db.as_json(sanitize=False)
  724. assert isinstance(dbjson, basestring) and len(dbjson) > 0
  725. unicode_keys = True
  726. if sys.version < "2.6.5":
  727. unicode_keys = False
  728. db3 = DAL(**serializers.loads_json(dbjson,
  729. unicode_keys=unicode_keys))
  730. assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\
  731. db3.person.uuid.type == db.person.uuid.type
  732. drop(db3.person)
  733. db3.commit()
  734. except ImportError:
  735. pass
  736. mpfc = "Monty Python's Flying Circus"
  737. dbdict4 = {"uri": DEFAULT_URI,
  738. "tables":[{"tablename": "tvshow",
  739. "fields": [{"fieldname": "name",
  740. "default":mpfc},
  741. {"fieldname": "rating",
  742. "type":"double"}]},
  743. {"tablename": "staff",
  744. "fields": [{"fieldname": "name",
  745. "default":"Michael"},
  746. {"fieldname": "food",
  747. "default":"Spam"},
  748. {"fieldname": "tvshow",
  749. "type": "reference tvshow"}]}]}
  750. db4 = DAL(**dbdict4)
  751. assert "staff" in db4.tables
  752. assert "name" in db4.staff
  753. assert db4.tvshow.rating.type == "double"
  754. assert (isinstance(db4.tvshow.insert(), long), isinstance(db4.tvshow.insert(name="Loriot"), long),
  755. isinstance(db4.tvshow.insert(name="Il Mattatore"), long)) == (True, True, True)
  756. assert isinstance(db4(db4.tvshow).select().first().id, long) == True
  757. assert db4(db4.tvshow).select().first().name == mpfc
  758. drop(db4.staff)
  759. drop(db4.tvshow)
  760. db4.commit()
  761. dbdict5 = {"uri": DEFAULT_URI}
  762. db5 = DAL(**dbdict5)
  763. assert db5.tables in ([], None)
  764. assert not (str(db5) in ("", None))
  765. dbdict6 = {"uri": DEFAULT_URI,
  766. "tables":[{"tablename": "staff"},
  767. {"tablename": "tvshow",
  768. "fields": [{"fieldname": "name"},
  769. {"fieldname": "rating", "type":"double"}
  770. ]
  771. }]
  772. }
  773. db6 = DAL(**dbdict6)
  774. assert len(db6["staff"].fields) == 1
  775. assert "name" in db6["tvshow"].fields
  776. assert db6.staff.insert() is not None
  777. assert isinstance(db6(db6.staff).select().first().id, long) == True
  778. drop(db6.staff)
  779. drop(db6.tvshow)
  780. db6.commit()
  781. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  782. class TestValidateAndInsert(unittest.TestCase):
  783. def testRun(self):
  784. import datetime
  785. from gluon.validators import IS_INT_IN_RANGE
  786. db = DAL(DEFAULT_URI, check_reserved=['all'])
  787. db.define_table('val_and_insert',
  788. Field('aa'),
  789. Field('bb', 'integer',
  790. requires=IS_INT_IN_RANGE(1,5))
  791. )
  792. rtn = db.val_and_insert.validate_and_insert(aa='test1', bb=2)
  793. self.assertEqual(isinstance(rtn.id, long), True)
  794. #errors should be empty
  795. self.assertEqual(len(rtn.errors.keys()), 0)
  796. #this insert won't pass
  797. rtn = db.val_and_insert.validate_and_insert(bb="a")
  798. #the returned id should be None
  799. self.assertEqual(rtn.id, None)
  800. #an error message should be in rtn.errors.bb
  801. self.assertNotEqual(rtn.errors.bb, None)
  802. #cleanup table
  803. drop(db.val_and_insert)
  804. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  805. class TestSelectAsDict(unittest.TestCase):
  806. def testSelect(self):
  807. db = DAL(DEFAULT_URI, check_reserved=['all'])
  808. db.define_table(
  809. 'a_table',
  810. Field('b_field'),
  811. Field('a_field'),
  812. )
  813. db.a_table.insert(a_field="aa1", b_field="bb1")
  814. rtn = db(db.a_table).select(db.a_table.id, db.a_table.b_field, db.a_table.a_field).as_list()
  815. self.assertEqual(rtn[0]['b_field'], 'bb1')
  816. keys = rtn[0].keys()
  817. self.assertEqual(len(keys), 3)
  818. self.assertEqual(("id" in keys, "b_field" in keys, "a_field" in keys), (True, True, True))
  819. drop(db.a_table)
  820. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  821. class TestRNameTable(unittest.TestCase):
  822. #tests for highly experimental rname attribute
  823. @unittest.skipIf(IS_MONGODB, "TODO: MongoDB assertion error (long object has no attribute id)")
  824. def testSelect(self):
  825. db = DAL(DEFAULT_URI, check_reserved=['all'])
  826. rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated tablename'
  827. db.define_table(
  828. 'easy_name',
  829. Field('a_field'),
  830. rname=rname
  831. )
  832. rtn = db.easy_name.insert(a_field='a')
  833. self.assertEqual(isinstance(rtn.id, long), True)
  834. rtn = db(db.easy_name.a_field == 'a').select()
  835. self.assertEqual(len(rtn), 1)
  836. self.assertEqual(isinstance(rtn[0].id, long), True)
  837. self.assertEqual(rtn[0].a_field, 'a')
  838. db.easy_name.insert(a_field='b')
  839. self.assertEqual(db(db.easy_name).count(), 2)
  840. rtn = db(db.easy_name.a_field == 'a').update(a_field='c')
  841. self.assertEqual(rtn, 1)
  842. #clean up
  843. drop(db.easy_name)
  844. @unittest.skip("JOIN queries are not supported")
  845. def testJoin(self):
  846. db = DAL(DEFAULT_URI, check_reserved=['all'])
  847. rname = db._adapter.QUOTE_TEMPLATE % 'this is table t1'
  848. rname2 = db._adapter.QUOTE_TEMPLATE % 'this is table t2'
  849. db.define_table('t1', Field('aa'), rname=rname)
  850. db.define_table('t2', Field('aa'), Field('b', db.t1), rname=rname2)
  851. i1 = db.t1.insert(aa='1')
  852. i2 = db.t1.insert(aa='2')
  853. i3 = db.t1.insert(aa='3')
  854. db.t2.insert(aa='4', b=i1)
  855. db.t2.insert(aa='5', b=i2)
  856. db.t2.insert(aa='6', b=i2)
  857. self.assertEqual(len(db(db.t1.id
  858. == db.t2.b).select(orderby=db.t1.aa
  859. | db.t2.aa)), 3)
  860. self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
  861. | db.t2.aa)[2].t1.aa, '2')
  862. self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
  863. | db.t2.aa)[2].t2.aa, '6')
  864. self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL,
  865. left=db.t2.on(db.t1.id == db.t2.b),
  866. orderby=db.t1.aa | db.t2.aa)), 4)
  867. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  868. left=db.t2.on(db.t1.id == db.t2.b),
  869. orderby=db.t1.aa | db.t2.aa)[2].t1.aa, '2')
  870. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  871. left=db.t2.on(db.t1.id == db.t2.b),
  872. orderby=db.t1.aa | db.t2.aa)[2].t2.aa, '6')
  873. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  874. left=db.t2.on(db.t1.id == db.t2.b),
  875. orderby=db.t1.aa | db.t2.aa)[3].t1.aa, '3')
  876. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  877. left=db.t2.on(db.t1.id == db.t2.b),
  878. orderby=db.t1.aa | db.t2.aa)[3].t2.aa, None)
  879. self.assertEqual(len(db().select(db.t1.aa, db.t2.id.count(),
  880. left=db.t2.on(db.t1.id == db.t2.b),
  881. orderby=db.t1.aa, groupby=db.t1.aa)),
  882. 3)
  883. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  884. left=db.t2.on(db.t1.id == db.t2.b),
  885. orderby=db.t1.aa,
  886. groupby=db.t1.aa)[0]._extra[db.t2.id.count()],
  887. 1)
  888. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  889. left=db.t2.on(db.t1.id == db.t2.b),
  890. orderby=db.t1.aa,
  891. groupby=db.t1.aa)[1]._extra[db.t2.id.count()],
  892. 2)
  893. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  894. left=db.t2.on(db.t1.id == db.t2.b),
  895. orderby=db.t1.aa,
  896. groupby=db.t1.aa)[2]._extra[db.t2.id.count()],
  897. 0)
  898. drop(db.t2)
  899. drop(db.t1)
  900. db.define_table('person',Field('name'), rname=rname)
  901. id = db.person.insert(name="max")
  902. self.assertEqual(id.name,'max')
  903. db.define_table('dog',Field('name'),Field('ownerperson','reference person'), rname=rname2)
  904. db.dog.insert(name='skipper',ownerperson=1)
  905. row = db(db.person.id==db.dog.ownerperson).select().first()
  906. self.assertEqual(row[db.person.name],'max')
  907. self.assertEqual(row['person.name'],'max')
  908. drop(db.dog)
  909. self.assertEqual(len(db.person._referenced_by),0)
  910. drop(db.person)
  911. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  912. class TestRNameFields(unittest.TestCase):
  913. # tests for highly experimental rname attribute
  914. @unittest.skipIf(IS_GAE or IS_MONGODB, 'TODO: Datastore throws unsupported error for AGGREGATE. MongoDB assertion error (long object has no attribute id)')
  915. def testSelect(self):
  916. db = DAL(DEFAULT_URI, check_reserved=['all'])
  917. rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated fieldname'
  918. rname2 = db._adapter.QUOTE_TEMPLATE % 'rrating from 1 to 10'
  919. db.define_table(
  920. 'easy_name',
  921. Field('a_field', rname=rname),
  922. Field('rating', 'integer', rname=rname2, default=2)
  923. )
  924. rtn = db.easy_name.insert(a_field='a')
  925. self.assertEqual(isinstance(rtn.id, long), True)
  926. rtn = db(db.easy_name.a_field == 'a').select()
  927. self.assertEqual(len(rtn), 1)
  928. self.assertEqual(isinstance(rtn[0].id, long), True)
  929. self.assertEqual(rtn[0].a_field, 'a')
  930. db.easy_name.insert(a_field='b')
  931. rtn = db(db.easy_name.id > 0).delete()
  932. self.assertEqual(rtn, 2)
  933. rtn = db(db.easy_name.id > 0).count()
  934. self.assertEqual(rtn, 0)
  935. db.easy_name.insert(a_field='a')
  936. db.easy_name.insert(a_field='b')
  937. rtn = db(db.easy_name.id > 0).count()
  938. self.assertEqual(rtn, 2)
  939. rtn = db(db.easy_name.a_field == 'a').update(a_field='c')
  940. rtn = db(db.easy_name.a_field == 'c').count()
  941. self.assertEqual(rtn, 1)
  942. rtn = db(db.easy_name.a_field != 'c').count()
  943. self.assertEqual(rtn, 1)
  944. avg = db.easy_name.id.avg()
  945. rtn = db(db.easy_name.id > 0).select(avg)
  946. self.assertEqual(rtn[0][avg], 3)
  947. avg = db.easy_name.rating.avg()
  948. rtn = db(db.easy_name.id > 0).select(avg)
  949. self.assertEqual(rtn[0][avg], 2)
  950. rname = db._adapter.QUOTE_TEMPLATE % 'this is the person name'
  951. db.define_table(
  952. 'person',
  953. Field('name', default="Michael", rname=rname),
  954. Field('uuid')
  955. )
  956. michael = db.person.insert() #default insert
  957. john = db.person.insert(name='John')
  958. luke = db.person.insert(name='Luke')
  959. self.assertEqual(len(rtn), 3)
  960. self.assertEqual(rtn[0].id, michael)
  961. self.assertEqual(rtn[0].name, 'Michael')
  962. self.assertEqual(rtn[1].id, john)
  963. self.assertEqual(rtn[1].name, 'John')
  964. #fetch owners, eventually with pet
  965. #main point is retrieving Luke with no pets
  966. rtn = db(db.person.id > 0).select()
  967. self.assertEqual(rtn[0].id, michael)
  968. self.assertEqual(rtn[0].name, 'Michael')
  969. self.assertEqual(rtn[3].name, 'Luke')
  970. self.assertEqual(rtn[3].id, luke)
  971. #as dict
  972. rtn = db(db.person.id > 0).select().as_dict()
  973. self.assertEqual(rtn[1]['name'], 'Michael')
  974. #as list
  975. rtn = db(db.person.id > 0).select().as_list()
  976. self.assertEqual(rtn[0]['name'], 'Michael')
  977. #isempty
  978. rtn = db(db.person.id > 0).isempty()
  979. self.assertEqual(rtn, False)
  980. #aliases
  981. rname = db._adapter.QUOTE_TEMPLATE % 'the cub name'
  982. if DEFAULT_URI.startswith('mssql'):
  983. #multiple cascade gotcha
  984. for key in ['reference','reference FK']:
  985. db._adapter.types[key]=db._adapter.types[key].replace(
  986. '%(on_delete_action)s','NO ACTION')
  987. db.define_table('pet_farm',
  988. Field('name', rname=rname),
  989. Field('father','reference pet_farm'),
  990. Field('mother','reference pet_farm'),
  991. )
  992. minali = db.pet_farm.insert(name='Minali')
  993. osbert = db.pet_farm.insert(name='Osbert')
  994. #they had a cub
  995. selina = db.pet_farm.insert(name='Selina', father=osbert, mother=minali)
  996. father = db.pet_farm.with_alias('father')
  997. mother = db.pet_farm.with_alias('mother')
  998. #fetch pets with relatives
  999. rtn = db().select(
  1000. db.pet_farm.name, father.name, mother.name,
  1001. left=[
  1002. father.on(father.id == db.pet_farm.father),
  1003. mother.on(mother.id == db.pet_farm.mother)
  1004. ],
  1005. orderby=db.pet_farm.id
  1006. )
  1007. self.assertEqual(len(rtn), 3)
  1008. self.assertEqual(rtn[0].pet_farm.name, 'Minali')
  1009. self.assertEqual(rtn[0].father.name, None)
  1010. self.assertEqual(rtn[0].mother.name, None)
  1011. self.assertEqual(rtn[1].pet_farm.name, 'Osbert')
  1012. self.assertEqual(rtn[2].pet_farm.name, 'Selina')
  1013. self.assertEqual(rtn[2].father.name, 'Osbert')
  1014. self.assertEqual(rtn[2].mother.name, 'Minali')
  1015. #clean up
  1016. drop(db.pet_farm)
  1017. drop(db.person)
  1018. drop(db.easy_name)
  1019. @unittest.skipIf(IS_GAE or IS_MONGODB, 'TODO: Datastore does not accept dict objects as json field input. MongoDB assertionerror Binary("x", 0) != "x"')
  1020. def testRun(self):
  1021. db = DAL(DEFAULT_URI, check_reserved=['all'])
  1022. rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated fieldname'
  1023. for ft in ['string', 'text', 'password', 'upload', 'blob']:
  1024. db.define_table('tt', Field('aa', ft, default='', rname=rname))
  1025. self.assertEqual(isinstance(db.tt.insert(aa='x'), long), True)
  1026. self.assertEqual(db().select(db.tt.aa)[0].aa, 'x')
  1027. drop(db.tt)
  1028. db.define_table('tt', Field('aa', 'integer', default=1, rname=rname))
  1029. self.assertEqual(isinstance(db.tt.insert(aa=3), long), True)
  1030. self.assertEqual(db().select(db.tt.aa)[0].aa, 3)
  1031. drop(db.tt)
  1032. db.define_table('tt', Field('aa', 'double', default=1, rname=rname))
  1033. self.assertEqual(isinstance(db.tt.insert(aa=3.1), long), True)
  1034. self.assertEqual(db().select(db.tt.aa)[0].aa, 3.1)
  1035. drop(db.tt)
  1036. db.define_table('tt', Field('aa', 'boolean', default=True, rname=rname))
  1037. self.assertEqual(isinstance(db.tt.insert(aa=True), long), True)
  1038. self.assertEqual(db().select(db.tt.aa)[0].aa, True)
  1039. drop(db.tt)
  1040. db.define_table('tt', Field('aa', 'json', default={}, rname=rname))
  1041. self.assertEqual(isinstance(db.tt.insert(aa={}), long), True)
  1042. self.assertEqual(db().select(db.tt.aa)[0].aa, {})
  1043. drop(db.tt)
  1044. db.define_table('tt', Field('aa', 'date',
  1045. default=datetime.date.today(), rname=rname))
  1046. t0 = datetime.date.today()
  1047. self.assertEqual(isinstance(db.tt.insert(aa=t0), long), True)
  1048. self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
  1049. drop(db.tt)
  1050. db.define_table('tt', Field('aa', 'datetime',
  1051. default=datetime.datetime.today(), rname=rname))
  1052. t0 = datetime.datetime(
  1053. 1971,
  1054. 12,
  1055. 21,
  1056. 10,
  1057. 30,
  1058. 55,
  1059. 0,
  1060. )
  1061. self.assertEqual(db.tt.insert(aa=t0), 1)
  1062. self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
  1063. ## Row APIs
  1064. row = db().select(db.tt.aa)[0]
  1065. self.assertEqual(db.tt[1].aa,t0)
  1066. self.assertEqual(db.tt['aa'],db.tt.aa)
  1067. self.assertEqual(db.tt(1).aa,t0)
  1068. self.assertTrue(db.tt(1,aa=None)==None)
  1069. self.assertFalse(db.tt(1,aa=t0)==None)
  1070. self.assertEqual(row.aa,t0)
  1071. self.assertEqual(row['aa'],t0)
  1072. self.assertEqual(row['tt.aa'],t0)
  1073. self.assertEqual(row('tt.aa'),t0)
  1074. ## Lazy and Virtual fields
  1075. db.tt.b = Field.Virtual(lambda row: row.tt.aa)
  1076. db.tt.c = Field.Lazy(lambda row: row.tt.aa)
  1077. row = db().select(db.tt.aa)[0]
  1078. self.assertEqual(row.b,t0)
  1079. self.assertEqual(row.c(),t0)
  1080. drop(db.tt)
  1081. db.define_table('tt', Field('aa', 'time', default='11:30', rname=rname))
  1082. t0 = datetime.time(10, 30, 55)
  1083. self.assertEqual(isinstance(db.tt.insert(aa=t0), long), True)
  1084. self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
  1085. drop(db.tt)
  1086. def testInsert(self):
  1087. db = DAL(DEFAULT_URI, check_reserved=['all'])
  1088. rname = db._adapter.QUOTE_TEMPLATE % 'a very complicated fieldname'
  1089. db.define_table('tt', Field('aa', rname=rname))
  1090. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  1091. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  1092. self.assertEqual(isinstance(db.tt.insert(aa='1'), long), True)
  1093. self.assertEqual(db(db.tt.aa == '1').count(), 3)
  1094. self.assertEqual(db(db.tt.aa == '2').isempty(), True)
  1095. self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3)
  1096. self.assertEqual(db(db.tt.aa == '2').count(), 3)
  1097. self.assertEqual(db(db.tt.aa == '2').isempty(), False)
  1098. self.assertEqual(db(db.tt.aa == '2').delete(), 3)
  1099. self.assertEqual(db(db.tt.aa == '2').isempty(), True)
  1100. drop(db.tt)
  1101. @unittest.skip("JOIN queries are not supported")
  1102. def testJoin(self):
  1103. db = DAL(DEFAULT_URI, check_reserved=['all'])
  1104. rname = db._adapter.QUOTE_TEMPLATE % 'this is field aa'
  1105. rname2 = db._adapter.QUOTE_TEMPLATE % 'this is field b'
  1106. db.define_table('t1', Field('aa', rname=rname))
  1107. db.define_table('t2', Field('aa', rname=rname), Field('b', db.t1, rname=rname2))
  1108. i1 = db.t1.insert(aa='1')
  1109. i2 = db.t1.insert(aa='2')
  1110. i3 = db.t1.insert(aa='3')
  1111. db.t2.insert(aa='4', b=i1)
  1112. db.t2.insert(aa='5', b=i2)
  1113. db.t2.insert(aa='6', b=i2)
  1114. self.assertEqual(len(db(db.t1.id
  1115. == db.t2.b).select(orderby=db.t1.aa
  1116. | db.t2.aa)), 3)
  1117. self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
  1118. | db.t2.aa)[2].t1.aa, '2')
  1119. self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
  1120. | db.t2.aa)[2].t2.aa, '6')
  1121. self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL,
  1122. left=db.t2.on(db.t1.id == db.t2.b),
  1123. orderby=db.t1.aa | db.t2.aa)), 4)
  1124. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  1125. left=db.t2.on(db.t1.id == db.t2.b),
  1126. orderby=db.t1.aa | db.t2.aa)[2].t1.aa, '2')
  1127. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  1128. left=db.t2.on(db.t1.id == db.t2.b),
  1129. orderby=db.t1.aa | db.t2.aa)[2].t2.aa, '6')
  1130. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  1131. left=db.t2.on(db.t1.id == db.t2.b),
  1132. orderby=db.t1.aa | db.t2.aa)[3].t1.aa, '3')
  1133. self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
  1134. left=db.t2.on(db.t1.id == db.t2.b),
  1135. orderby=db.t1.aa | db.t2.aa)[3].t2.aa, None)
  1136. self.assertEqual(len(db().select(db.t1.aa, db.t2.id.count(),
  1137. left=db.t2.on(db.t1.id == db.t2.b),
  1138. orderby=db.t1.aa, groupby=db.t1.aa)),
  1139. 3)
  1140. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  1141. left=db.t2.on(db.t1.id == db.t2.b),
  1142. orderby=db.t1.aa,
  1143. groupby=db.t1.aa)[0]._extra[db.t2.id.count()],
  1144. 1)
  1145. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  1146. left=db.t2.on(db.t1.id == db.t2.b),
  1147. orderby=db.t1.aa,
  1148. groupby=db.t1.aa)[1]._extra[db.t2.id.count()],
  1149. 2)
  1150. self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
  1151. left=db.t2.on(db.t1.id == db.t2.b),
  1152. orderby=db.t1.aa,
  1153. groupby=db.t1.aa)[2]._extra[db.t2.id.count()],
  1154. 0)
  1155. drop(db.t2)
  1156. drop(db.t1)
  1157. db.define_table('person',Field('name', rname=rname))
  1158. id = db.person.insert(name="max")
  1159. self.assertEqual(id.name,'max')
  1160. db.define_table('dog',Field('name', rname=rname),Field('ownerperson','reference person', rname=rname2))
  1161. db.dog.insert(name='skipper',ownerperson=1)
  1162. row = db(db.person.id==db.dog.ownerperson).select().first()
  1163. self.assertEqual(row[db.person.name],'max')
  1164. self.assertEqual(row['person.name'],'max')
  1165. drop(db.dog)
  1166. self.assertEqual(len(db.person._referenced_by),0)
  1167. drop(db.person)
  1168. @unittest.skipIf(IS_IMAP, "TODO: IMAP test")
  1169. class TestQuoting(unittest.TestCase):
  1170. # tests for case sensitivity
  1171. def testCase(self):
  1172. return
  1173. db = DAL(DEFAULT_URI, check_reserved=['all'], ignore_field_case=False)
  1174. if DEFAULT_URI.startswith('mssql'):
  1175. #multiple cascade gotcha
  1176. for key in ['reference','reference FK']:
  1177. db._adapter.types[key]=db._adapter.types[key].replace(
  1178. '%(on_delete_action)s','NO ACTION')
  1179. # test table case
  1180. t0 = db.define_table('B',
  1181. Field('f', 'string'))
  1182. try:
  1183. t1 = db.define_table('b',
  1184. Field('B', t0),
  1185. Field('words', 'text'))
  1186. except Exception, e:
  1187. # An error is expected when database does not support case
  1188. # sensitive entity names.
  1189. if DEFAULT_URI.startswith('sqlite:'):
  1190. self.assertTrue(isinstance(e, db._adapter.driver.OperationalError))
  1191. return
  1192. raise e
  1193. blather = 'blah blah and so'
  1194. t0[0] = {'f': 'content'}
  1195. t1[0] = {'B': int(t0[1]['id']),
  1196. 'words': blather}
  1197. r = db(db.B.id==db.b.B).select()
  1198. self.assertEqual(r[0].b.words, blather)
  1199. drop(t1)
  1200. drop(t0)
  1201. # test field case
  1202. try:
  1203. t0 = db.define_table('table is a test',
  1204. Field('a_a'),
  1205. Field('a_A'))
  1206. except Exception, e:
  1207. # some db does not support case sensitive field names mysql is one of them.
  1208. if DEFAULT_URI.startswith('mysql:'):
  1209. db.rollback()
  1210. return
  1211. raise e
  1212. t0[0] = dict(a_a = 'a_a', a_A='a_A')
  1213. self.assertEqual(t0[1].a_a, 'a_a')
  1214. self.assertEqual(t0[1].a_A, 'a_A')
  1215. drop(t0)
  1216. def testPKFK(self):
  1217. # test primary keys
  1218. db = DAL(DEFAULT_URI, check_reserved=['all'], ignore_field_case=False)
  1219. if DEFAULT_URI.startswith('mssql'):
  1220. #multiple cascade gotcha
  1221. for key in ['reference','reference FK']:
  1222. db._adapter.types[key]=db._adapter.types[key].replace(
  1223. '%(on_delete_action)s','NO ACTION')
  1224. # test table without surrogate key. Length must is limited to
  1225. # 100 because of MySQL limitations: it cannot handle more than
  1226. # 767 bytes in unique keys.
  1227. t0 = db.define_table('t0', Field('Code', length=100), primarykey=['Code'])
  1228. t2 = db.define_table('t2', Field('f'), Field('t0_Code', 'reference t0'))
  1229. t3 = db.define_table('t3', Field('f', length=100), Field('t0_Code', t0.Code), primarykey=['f'])
  1230. t4 = db.define_table('t4', Field('f', length=100), Field('t0', t0), primarykey=['f'])
  1231. try:
  1232. t5 = db.define_table('t5', Field('f', length=100), Field('t0', 'reference no_table_wrong_reference'), primarykey=['f'])
  1233. except Exception, e:
  1234. self.assertTrue(isinstance(e, KeyError))
  1235. if DEFAULT_URI.startswith('mssql'):
  1236. #there's no drop cascade in mssql
  1237. drop(t3)
  1238. drop(t4)
  1239. drop(t2)
  1240. drop(t0)
  1241. else:
  1242. drop(t0, 'cascade')
  1243. drop(t2)
  1244. drop(t3)
  1245. drop(t4)
  1246. class TestTableAndFieldCase(unittest.TestCase):
  1247. """
  1248. at the Python level we should not allow db.C and db.c because of .table conflicts on windows
  1249. but it should be possible to map two different names into distinct tables "c" and "C" at the Python level
  1250. By default Python models names should be mapped into lower case table names and assume case insensitivity.
  1251. """
  1252. def testme(self):
  1253. return
  1254. class TestQuotesByDefault(unittest.TestCase):
  1255. """
  1256. all default tables names should be quoted unless an explicit mapping has been given for a table.
  1257. """
  1258. def testme(self):
  1259. return
  1260. if __name__ == '__main__':
  1261. unittest.main()
  1262. tearDownModule()