sqlobject 3.13.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlobject/__init__.py +15 -0
- sqlobject/__version__.py +8 -0
- sqlobject/boundattributes.py +124 -0
- sqlobject/cache.py +376 -0
- sqlobject/classregistry.py +138 -0
- sqlobject/col.py +2187 -0
- sqlobject/compat.py +59 -0
- sqlobject/conftest.py +64 -0
- sqlobject/constraints.py +77 -0
- sqlobject/converters.py +278 -0
- sqlobject/dbconnection.py +1137 -0
- sqlobject/dberrors.py +54 -0
- sqlobject/declarative.py +211 -0
- sqlobject/events.py +365 -0
- sqlobject/firebird/__init__.py +8 -0
- sqlobject/firebird/firebirdconnection.py +463 -0
- sqlobject/include/__init__.py +1 -0
- sqlobject/include/hashcol.py +109 -0
- sqlobject/include/tests/__init__.py +1 -0
- sqlobject/include/tests/test_hashcol.py +84 -0
- sqlobject/index.py +189 -0
- sqlobject/inheritance/__init__.py +551 -0
- sqlobject/inheritance/iteration.py +106 -0
- sqlobject/inheritance/tests/__init__.py +1 -0
- sqlobject/inheritance/tests/test_aggregates.py +23 -0
- sqlobject/inheritance/tests/test_asdict.py +48 -0
- sqlobject/inheritance/tests/test_deep_inheritance.py +114 -0
- sqlobject/inheritance/tests/test_destroy_cascade.py +23 -0
- sqlobject/inheritance/tests/test_foreignKey.py +94 -0
- sqlobject/inheritance/tests/test_indexes.py +42 -0
- sqlobject/inheritance/tests/test_inheritance.py +164 -0
- sqlobject/inheritance/tests/test_inheritance_tree.py +50 -0
- sqlobject/joins.py +656 -0
- sqlobject/main.py +1880 -0
- sqlobject/manager/__init__.py +1 -0
- sqlobject/manager/command.py +1309 -0
- sqlobject/maxdb/__init__.py +8 -0
- sqlobject/maxdb/maxdbconnection.py +320 -0
- sqlobject/maxdb/readme.txt +27 -0
- sqlobject/mssql/__init__.py +8 -0
- sqlobject/mssql/mssqlconnection.py +417 -0
- sqlobject/mysql/__init__.py +8 -0
- sqlobject/mysql/mysqlconnection.py +562 -0
- sqlobject/postgres/__init__.py +8 -0
- sqlobject/postgres/pgconnection.py +619 -0
- sqlobject/sqlbuilder.py +1655 -0
- sqlobject/sqlite/__init__.py +8 -0
- sqlobject/sqlite/sqliteconnection.py +418 -0
- sqlobject/sresults.py +377 -0
- sqlobject/styles.py +174 -0
- sqlobject/sybase/__init__.py +8 -0
- sqlobject/sybase/sybaseconnection.py +183 -0
- sqlobject/tests/__init__.py +1 -0
- sqlobject/tests/dbtest.py +363 -0
- sqlobject/tests/test_ForeignKey.py +133 -0
- sqlobject/tests/test_ForeignKey_cascade.py +150 -0
- sqlobject/tests/test_NoneValuedResultItem.py +33 -0
- sqlobject/tests/test_SQLMultipleJoin.py +65 -0
- sqlobject/tests/test_SQLRelatedJoin.py +114 -0
- sqlobject/tests/test_SingleJoin.py +30 -0
- sqlobject/tests/test_aggregates.py +75 -0
- sqlobject/tests/test_aliases.py +49 -0
- sqlobject/tests/test_asdict.py +18 -0
- sqlobject/tests/test_auto.py +218 -0
- sqlobject/tests/test_basic.py +359 -0
- sqlobject/tests/test_blob.py +34 -0
- sqlobject/tests/test_boundattributes.py +67 -0
- sqlobject/tests/test_cache.py +57 -0
- sqlobject/tests/test_class_hash.py +22 -0
- sqlobject/tests/test_columns_order.py +18 -0
- sqlobject/tests/test_combining_joins.py +47 -0
- sqlobject/tests/test_comparison.py +24 -0
- sqlobject/tests/test_compat.py +10 -0
- sqlobject/tests/test_complex_sorting.py +77 -0
- sqlobject/tests/test_constraints.py +43 -0
- sqlobject/tests/test_converters.py +276 -0
- sqlobject/tests/test_create_drop.py +29 -0
- sqlobject/tests/test_csvexport.py +101 -0
- sqlobject/tests/test_csvimport.py +21 -0
- sqlobject/tests/test_cyclic_reference.py +55 -0
- sqlobject/tests/test_datetime.py +171 -0
- sqlobject/tests/test_dbconnection.py +17 -0
- sqlobject/tests/test_decimal.py +63 -0
- sqlobject/tests/test_declarative.py +83 -0
- sqlobject/tests/test_default_style.py +65 -0
- sqlobject/tests/test_delete.py +74 -0
- sqlobject/tests/test_distinct.py +36 -0
- sqlobject/tests/test_empty.py +22 -0
- sqlobject/tests/test_enum.py +75 -0
- sqlobject/tests/test_events.py +142 -0
- sqlobject/tests/test_exceptions.py +40 -0
- sqlobject/tests/test_expire.py +29 -0
- sqlobject/tests/test_groupBy.py +45 -0
- sqlobject/tests/test_identity.py +29 -0
- sqlobject/tests/test_indexes.py +100 -0
- sqlobject/tests/test_inheritance.py +35 -0
- sqlobject/tests/test_joins.py +160 -0
- sqlobject/tests/test_joins_conditional.py +143 -0
- sqlobject/tests/test_jsonbcol.py +53 -0
- sqlobject/tests/test_jsoncol.py +56 -0
- sqlobject/tests/test_lazy.py +165 -0
- sqlobject/tests/test_md5.py +13 -0
- sqlobject/tests/test_mysql.py +50 -0
- sqlobject/tests/test_new_joins.py +165 -0
- sqlobject/tests/test_parse_uri.py +147 -0
- sqlobject/tests/test_paste.py +92 -0
- sqlobject/tests/test_perConnection.py +20 -0
- sqlobject/tests/test_pickle.py +42 -0
- sqlobject/tests/test_picklecol.py +44 -0
- sqlobject/tests/test_postgres.py +82 -0
- sqlobject/tests/test_reparent_sqlmeta.py +33 -0
- sqlobject/tests/test_schema.py +30 -0
- sqlobject/tests/test_select.py +218 -0
- sqlobject/tests/test_select_through.py +63 -0
- sqlobject/tests/test_setters.py +23 -0
- sqlobject/tests/test_slice.py +59 -0
- sqlobject/tests/test_sorting.py +80 -0
- sqlobject/tests/test_sqlbuilder.py +134 -0
- sqlobject/tests/test_sqlbuilder_dbspecific.py +66 -0
- sqlobject/tests/test_sqlbuilder_importproxy.py +55 -0
- sqlobject/tests/test_sqlbuilder_joins_instances.py +90 -0
- sqlobject/tests/test_sqlite.py +160 -0
- sqlobject/tests/test_sqlmeta_idName.py +52 -0
- sqlobject/tests/test_sqlobject_admin.py +38 -0
- sqlobject/tests/test_string_id.py +79 -0
- sqlobject/tests/test_style.py +36 -0
- sqlobject/tests/test_subqueries.py +142 -0
- sqlobject/tests/test_transactions.py +122 -0
- sqlobject/tests/test_unicode.py +84 -0
- sqlobject/tests/test_uuidcol.py +40 -0
- sqlobject/tests/test_validation.py +125 -0
- sqlobject/tests/test_views.py +159 -0
- sqlobject/util/__init__.py +1 -0
- sqlobject/util/csvexport.py +210 -0
- sqlobject/util/csvimport.py +366 -0
- sqlobject/util/moduleloader.py +35 -0
- sqlobject/util/threadinglocal.py +6 -0
- sqlobject/versioning/__init__.py +120 -0
- sqlobject/versioning/test/__init__.py +0 -0
- sqlobject/versioning/test/test_version.py +183 -0
- sqlobject/views.py +151 -0
- sqlobject/wsgi_middleware.py +109 -0
- sqlobject-3.13.1.data/scripts/sqlobject-admin +36 -0
- sqlobject-3.13.1.data/scripts/sqlobject-convertOldURI +19 -0
- sqlobject-3.13.1.dist-info/METADATA +177 -0
- sqlobject-3.13.1.dist-info/RECORD +149 -0
- sqlobject-3.13.1.dist-info/WHEEL +6 -0
- sqlobject-3.13.1.dist-info/entry_points.txt +4 -0
- sqlobject-3.13.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from sqlobject import SQLObject, SQLObjectNotFound, StringCol
|
|
3
|
+
from sqlobject.tests.dbtest import raises, setupClass, supports
|
|
4
|
+
from sqlobject import events
|
|
5
|
+
from sqlobject.main import sqlmeta
|
|
6
|
+
|
|
7
|
+
########################################
|
|
8
|
+
# Transaction test
|
|
9
|
+
########################################
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
support_transactions = supports('transactions')
|
|
14
|
+
except NameError:
|
|
15
|
+
# The module was imported during documentation building
|
|
16
|
+
pass
|
|
17
|
+
else:
|
|
18
|
+
if not support_transactions:
|
|
19
|
+
pytestmark = pytest.mark.skip("These tests require transactions")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SOTestSOTrans(SQLObject):
|
|
23
|
+
class sqlmeta:
|
|
24
|
+
defaultOrder = 'name'
|
|
25
|
+
name = StringCol(length=10, alternateID=True, dbName='name_col')
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def make_watcher():
|
|
29
|
+
log = []
|
|
30
|
+
|
|
31
|
+
def watch(*args):
|
|
32
|
+
log.append(args)
|
|
33
|
+
|
|
34
|
+
watch.log = log
|
|
35
|
+
return watch
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def make_listen(signal):
|
|
39
|
+
watcher = make_watcher()
|
|
40
|
+
events.listen(watcher, sqlmeta, signal)
|
|
41
|
+
return watcher
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_transaction():
|
|
45
|
+
commit_watcher = make_listen(events.CommitSignal)
|
|
46
|
+
rollback_watcher = make_listen(events.RollbackSignal)
|
|
47
|
+
setupClass(SOTestSOTrans)
|
|
48
|
+
SOTestSOTrans(name='bob')
|
|
49
|
+
SOTestSOTrans(name='tim')
|
|
50
|
+
trans = SOTestSOTrans._connection.transaction()
|
|
51
|
+
try:
|
|
52
|
+
SOTestSOTrans._connection.autoCommit = 'exception'
|
|
53
|
+
SOTestSOTrans(name='joe', connection=trans)
|
|
54
|
+
trans.rollback()
|
|
55
|
+
trans.begin()
|
|
56
|
+
assert ([n.name for n in SOTestSOTrans.select(connection=trans)]
|
|
57
|
+
== ['bob', 'tim'])
|
|
58
|
+
b = SOTestSOTrans.byName('bob', connection=trans)
|
|
59
|
+
b.name = 'robert'
|
|
60
|
+
trans.commit()
|
|
61
|
+
assert b.name == 'robert'
|
|
62
|
+
b.name = 'bob'
|
|
63
|
+
trans.rollback()
|
|
64
|
+
trans.begin()
|
|
65
|
+
assert b.name == 'robert'
|
|
66
|
+
assert len(commit_watcher.log) == 1
|
|
67
|
+
assert commit_watcher.log[0][0][0][0] == 'SOTestSOTrans'
|
|
68
|
+
assert commit_watcher.log[0][0][0][1] == [1, 2]
|
|
69
|
+
assert len(rollback_watcher.log) == 2
|
|
70
|
+
assert rollback_watcher.log[0][0][0][0] == 'SOTestSOTrans'
|
|
71
|
+
assert rollback_watcher.log[0][0][0][1] == [3]
|
|
72
|
+
assert rollback_watcher.log[1][0][0][0] == 'SOTestSOTrans'
|
|
73
|
+
assert rollback_watcher.log[1][0][0][1] == [1, 2]
|
|
74
|
+
finally:
|
|
75
|
+
SOTestSOTrans._connection.autoCommit = True
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_transaction_commit_sync():
|
|
79
|
+
setupClass(SOTestSOTrans)
|
|
80
|
+
connection = SOTestSOTrans._connection
|
|
81
|
+
trans = connection.transaction()
|
|
82
|
+
try:
|
|
83
|
+
SOTestSOTrans(name='bob')
|
|
84
|
+
bOut = SOTestSOTrans.byName('bob')
|
|
85
|
+
bIn = SOTestSOTrans.byName('bob', connection=trans)
|
|
86
|
+
bIn.name = 'robert'
|
|
87
|
+
assert bOut.name == 'bob'
|
|
88
|
+
trans.commit()
|
|
89
|
+
assert bOut.name == 'robert'
|
|
90
|
+
finally:
|
|
91
|
+
trans.rollback()
|
|
92
|
+
connection.autoCommit = True
|
|
93
|
+
connection.close()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_transaction_delete(close=False):
|
|
97
|
+
setupClass(SOTestSOTrans)
|
|
98
|
+
connection = SOTestSOTrans._connection
|
|
99
|
+
if (connection.dbName == 'sqlite') and connection._memory:
|
|
100
|
+
pytest.skip("The test doesn't work with sqlite memory connection")
|
|
101
|
+
trans = connection.transaction()
|
|
102
|
+
try:
|
|
103
|
+
SOTestSOTrans(name='bob')
|
|
104
|
+
bIn = SOTestSOTrans.byName('bob', connection=trans)
|
|
105
|
+
bIn.destroySelf()
|
|
106
|
+
bOut = SOTestSOTrans.select(SOTestSOTrans.q.name == 'bob')
|
|
107
|
+
assert bOut.count() == 1
|
|
108
|
+
bOutInst = bOut[0]
|
|
109
|
+
bOutID = bOutInst.id # noqa: bOutID is used in the string code below
|
|
110
|
+
trans.commit(close=close)
|
|
111
|
+
assert bOut.count() == 0
|
|
112
|
+
raises(SQLObjectNotFound, SOTestSOTrans.get, bOutID)
|
|
113
|
+
with raises(SQLObjectNotFound):
|
|
114
|
+
bOutInst.name
|
|
115
|
+
finally:
|
|
116
|
+
trans.rollback()
|
|
117
|
+
connection.autoCommit = True
|
|
118
|
+
connection.close()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_transaction_delete_with_close():
|
|
122
|
+
test_transaction_delete(close=True)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from sqlobject import IntCol, SQLObject, UnicodeCol
|
|
2
|
+
from sqlobject.compat import PY2
|
|
3
|
+
from sqlobject.tests.dbtest import setupClass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
########################################
|
|
7
|
+
# Unicode columns
|
|
8
|
+
########################################
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SOTestUnicode(SQLObject):
|
|
12
|
+
so_count = IntCol(alternateID=True)
|
|
13
|
+
col = UnicodeCol(alternateID=True, length=100)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
data = [u'\u00f0', u'test', 'ascii test']
|
|
17
|
+
items = []
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def setup():
|
|
21
|
+
global items
|
|
22
|
+
items = []
|
|
23
|
+
setupClass(SOTestUnicode)
|
|
24
|
+
for i, s in enumerate(data):
|
|
25
|
+
items.append(SOTestUnicode(so_count=i, col=s))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_create():
|
|
29
|
+
setup()
|
|
30
|
+
for s, item in zip(data, items):
|
|
31
|
+
assert item.col == s
|
|
32
|
+
|
|
33
|
+
conn = SOTestUnicode._connection
|
|
34
|
+
if PY2:
|
|
35
|
+
rows = conn.queryAll("""
|
|
36
|
+
SELECT so_count, col
|
|
37
|
+
FROM so_test_unicode
|
|
38
|
+
ORDER BY so_count
|
|
39
|
+
""")
|
|
40
|
+
for so_count, col in rows:
|
|
41
|
+
if not isinstance(col, bytes):
|
|
42
|
+
col = col.encode('utf-8')
|
|
43
|
+
assert data[so_count].encode('utf-8') == col
|
|
44
|
+
else:
|
|
45
|
+
rows = conn.queryAll("""
|
|
46
|
+
SELECT so_count, col
|
|
47
|
+
FROM so_test_unicode
|
|
48
|
+
ORDER BY so_count
|
|
49
|
+
""")
|
|
50
|
+
# On python 3, everthings already decoded to unicode
|
|
51
|
+
for so_count, col in rows:
|
|
52
|
+
assert data[so_count] == col
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_select():
|
|
56
|
+
setup()
|
|
57
|
+
for i, value in enumerate(data):
|
|
58
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col == value))
|
|
59
|
+
assert len(rows) == 1
|
|
60
|
+
if PY2:
|
|
61
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col == value))
|
|
62
|
+
assert len(rows) == 1
|
|
63
|
+
rows = list(SOTestUnicode.selectBy(col=value))
|
|
64
|
+
assert len(rows) == 1
|
|
65
|
+
if PY2:
|
|
66
|
+
rows = list(SOTestUnicode.selectBy(col=value))
|
|
67
|
+
assert len(rows) == 1
|
|
68
|
+
row = SOTestUnicode.byCol(value)
|
|
69
|
+
assert row.so_count == i
|
|
70
|
+
|
|
71
|
+
# starts/endswith/contains
|
|
72
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col.startswith("test")))
|
|
73
|
+
assert len(rows) == 1
|
|
74
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col.endswith("test")))
|
|
75
|
+
assert len(rows) == 2
|
|
76
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col.contains("test")))
|
|
77
|
+
assert len(rows) == 2
|
|
78
|
+
rows = list(SOTestUnicode.select(
|
|
79
|
+
SOTestUnicode.q.col.startswith(u"\u00f0")))
|
|
80
|
+
assert len(rows) == 1
|
|
81
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col.endswith(u"\u00f0")))
|
|
82
|
+
assert len(rows) == 1
|
|
83
|
+
rows = list(SOTestUnicode.select(SOTestUnicode.q.col.contains(u"\u00f0")))
|
|
84
|
+
assert len(rows) == 1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
from sqlobject import SQLObject, UuidCol
|
|
3
|
+
from sqlobject.tests.dbtest import setupClass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
########################################
|
|
7
|
+
# Uuid columns
|
|
8
|
+
########################################
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
testuuid = UUID('7e3b5c1e-3402-4b10-a3c6-8ee6dbac7d1a')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UuidContainer(SQLObject):
|
|
15
|
+
uuiddata = UuidCol(alternateID=True, default=None)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_uuidCol():
|
|
19
|
+
setupClass([UuidContainer])
|
|
20
|
+
|
|
21
|
+
my_uuid = UuidContainer(uuiddata=testuuid)
|
|
22
|
+
iid = my_uuid.id
|
|
23
|
+
|
|
24
|
+
UuidContainer._connection.cache.clear()
|
|
25
|
+
|
|
26
|
+
my_uuid_2 = UuidContainer.get(iid)
|
|
27
|
+
|
|
28
|
+
assert my_uuid_2.uuiddata == testuuid
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_alternate_id():
|
|
32
|
+
setupClass([UuidContainer])
|
|
33
|
+
|
|
34
|
+
UuidContainer(uuiddata=testuuid)
|
|
35
|
+
|
|
36
|
+
UuidContainer._connection.cache.clear()
|
|
37
|
+
|
|
38
|
+
my_uuid_2 = UuidContainer.byUuiddata(testuuid)
|
|
39
|
+
|
|
40
|
+
assert my_uuid_2.uuiddata == testuuid
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from sqlobject import BoolCol, FloatCol, IntCol, PickleCol, SQLObject, \
|
|
2
|
+
StringCol, UnicodeCol
|
|
3
|
+
from sqlobject.col import validators
|
|
4
|
+
from sqlobject.compat import PY2
|
|
5
|
+
from sqlobject.tests.dbtest import raises, setupClass
|
|
6
|
+
|
|
7
|
+
if not PY2:
|
|
8
|
+
# alias for python 3 compatability
|
|
9
|
+
long = int
|
|
10
|
+
|
|
11
|
+
########################################
|
|
12
|
+
# Validation/conversion
|
|
13
|
+
########################################
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SOTestValidator(validators.Validator):
|
|
17
|
+
def to_python(self, value, state):
|
|
18
|
+
if value:
|
|
19
|
+
self.save_value.append(value)
|
|
20
|
+
return 1
|
|
21
|
+
return value
|
|
22
|
+
|
|
23
|
+
def from_python(self, value, state):
|
|
24
|
+
if value:
|
|
25
|
+
self.save_value.append(value)
|
|
26
|
+
return 2
|
|
27
|
+
return value
|
|
28
|
+
|
|
29
|
+
validator1 = SOTestValidator(save_value=[])
|
|
30
|
+
validator2 = SOTestValidator(save_value=[])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SOValidation(SQLObject):
|
|
34
|
+
|
|
35
|
+
name = StringCol(validator=validators.PlainText(),
|
|
36
|
+
default='x', dbName='name_col')
|
|
37
|
+
name2 = StringCol(validator2=validators.ConfirmType(type=str), default='y')
|
|
38
|
+
name3 = IntCol(validator=validators.Wrapper(fromPython=int), default=100)
|
|
39
|
+
name4 = FloatCol(default=2.718)
|
|
40
|
+
name5 = PickleCol(default=None)
|
|
41
|
+
name6 = BoolCol(default=None)
|
|
42
|
+
name7 = UnicodeCol(default=None)
|
|
43
|
+
name8 = IntCol(default=None)
|
|
44
|
+
name9 = IntCol(validator=validator1, validator2=validator2, default=0)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SOValidationTest(object):
|
|
48
|
+
def __init__(self, value):
|
|
49
|
+
self.value = value
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if PY2:
|
|
53
|
+
class SOValidationTestUnicode(SOValidationTest):
|
|
54
|
+
def __unicode__(self):
|
|
55
|
+
return self.value
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SOValidationTestInt(SOValidationTest):
|
|
59
|
+
def __int__(self):
|
|
60
|
+
return self.value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class SOValidationTestBool(SOValidationTest):
|
|
64
|
+
def __nonzero__(self):
|
|
65
|
+
return self.value
|
|
66
|
+
__bool__ = __nonzero__
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class SOValidationTestFloat(SOValidationTest):
|
|
70
|
+
def __float__(self):
|
|
71
|
+
return self.value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TestValidation:
|
|
75
|
+
|
|
76
|
+
def setup_method(self, meth):
|
|
77
|
+
setupClass(SOValidation)
|
|
78
|
+
|
|
79
|
+
def test_validate(self):
|
|
80
|
+
t = SOValidation(name='hey')
|
|
81
|
+
raises(validators.Invalid, setattr, t, 'name', '!!!')
|
|
82
|
+
t.name = 'you'
|
|
83
|
+
assert t.name == 'you'
|
|
84
|
+
|
|
85
|
+
def test_confirmType(self):
|
|
86
|
+
t = SOValidation(name2='hey')
|
|
87
|
+
raises(validators.Invalid, setattr, t, 'name2', 1)
|
|
88
|
+
raises(validators.Invalid, setattr, t, 'name3', '1')
|
|
89
|
+
raises(validators.Invalid, setattr, t, 'name4', '1')
|
|
90
|
+
if t._connection.dbName != 'postgres' or \
|
|
91
|
+
t._connection.driver not in ('odbc', 'pyodbc', 'pypyodbc'):
|
|
92
|
+
raises(validators.Invalid, setattr, t, 'name6', '1')
|
|
93
|
+
raises(validators.Invalid, setattr, t, 'name7', 1)
|
|
94
|
+
t.name2 = 'you'
|
|
95
|
+
assert t.name2 == 'you'
|
|
96
|
+
|
|
97
|
+
for name, cls, value in (
|
|
98
|
+
('name4', SOValidationTestFloat, 1.1),
|
|
99
|
+
('name6', SOValidationTestBool, True),
|
|
100
|
+
('name8', SOValidationTestInt, 1)):
|
|
101
|
+
setattr(t, name, cls(value))
|
|
102
|
+
assert getattr(t, name) == value
|
|
103
|
+
if PY2:
|
|
104
|
+
for name, cls, value in (
|
|
105
|
+
('name7', SOValidationTestUnicode, u'test'),):
|
|
106
|
+
setattr(t, name, cls(value))
|
|
107
|
+
assert getattr(t, name) == value
|
|
108
|
+
|
|
109
|
+
def test_wrapType(self):
|
|
110
|
+
t = SOValidation(name3=1)
|
|
111
|
+
raises(validators.Invalid, setattr, t, 'name3', 'x')
|
|
112
|
+
t.name3 = long(1)
|
|
113
|
+
assert t.name3 == 1
|
|
114
|
+
t.name3 = 0
|
|
115
|
+
assert t.name3 == 0
|
|
116
|
+
|
|
117
|
+
def test_emptyValue(self):
|
|
118
|
+
t = SOValidation(name5={})
|
|
119
|
+
assert t.name5 == {}
|
|
120
|
+
|
|
121
|
+
def test_validator2(self):
|
|
122
|
+
SOValidation(name9=1)
|
|
123
|
+
SOValidation(name9=2)
|
|
124
|
+
assert validator1.save_value == [2, 2, 2, 2, 2, 2]
|
|
125
|
+
assert validator2.save_value == [1, 1, 1, 2, 1, 1]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from sqlobject import ForeignKey, IntCol, SQLMultipleJoin, SQLObject, \
|
|
2
|
+
StringCol, func
|
|
3
|
+
from sqlobject.views import ViewSQLObject
|
|
4
|
+
from sqlobject.tests.dbtest import inserts, setupClass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PhoneNumber(SQLObject):
|
|
8
|
+
number = StringCol()
|
|
9
|
+
calls = SQLMultipleJoin('PhoneCall')
|
|
10
|
+
incoming = SQLMultipleJoin('PhoneCall', joinColumn='toID')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PhoneCall(SQLObject):
|
|
14
|
+
phoneNumber = ForeignKey('PhoneNumber')
|
|
15
|
+
to = ForeignKey('PhoneNumber')
|
|
16
|
+
minutes = IntCol()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ViewPhoneCall(ViewSQLObject):
|
|
20
|
+
class sqlmeta:
|
|
21
|
+
idName = PhoneCall.q.id
|
|
22
|
+
clause = PhoneCall.q.phoneNumberID == PhoneNumber.q.id
|
|
23
|
+
|
|
24
|
+
minutes = IntCol(dbName=PhoneCall.q.minutes)
|
|
25
|
+
number = StringCol(dbName=PhoneNumber.q.number)
|
|
26
|
+
phoneNumber = ForeignKey('PhoneNumber', dbName=PhoneNumber.q.id)
|
|
27
|
+
call = ForeignKey('PhoneCall', dbName=PhoneCall.q.id)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ViewPhone(ViewSQLObject):
|
|
31
|
+
class sqlmeta:
|
|
32
|
+
idName = PhoneNumber.q.id
|
|
33
|
+
clause = PhoneCall.q.phoneNumberID == PhoneNumber.q.id
|
|
34
|
+
|
|
35
|
+
minutes = IntCol(dbName=func.SUM(PhoneCall.q.minutes))
|
|
36
|
+
numberOfCalls = IntCol(dbName=func.COUNT(PhoneCall.q.phoneNumberID))
|
|
37
|
+
number = StringCol(dbName=PhoneNumber.q.number)
|
|
38
|
+
phoneNumber = ForeignKey('PhoneNumber', dbName=PhoneNumber.q.id)
|
|
39
|
+
calls = SQLMultipleJoin('PhoneCall', joinColumn='phoneNumberID')
|
|
40
|
+
vCalls = SQLMultipleJoin('ViewPhoneCall', joinColumn='phoneNumberID',
|
|
41
|
+
orderBy='id')
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ViewPhoneMore(ViewSQLObject):
|
|
45
|
+
''' View on top of view '''
|
|
46
|
+
class sqlmeta:
|
|
47
|
+
idName = ViewPhone.q.id
|
|
48
|
+
clause = ViewPhone.q.id == PhoneCall.q.toID
|
|
49
|
+
|
|
50
|
+
number = StringCol(dbName=ViewPhone.q.number)
|
|
51
|
+
timesCalled = IntCol(dbName=func.COUNT(PhoneCall.q.toID))
|
|
52
|
+
timesCalledLong = IntCol(dbName=func.COUNT(PhoneCall.q.toID))
|
|
53
|
+
timesCalledLong.aggregateClause = PhoneCall.q.minutes > 10
|
|
54
|
+
minutesCalled = IntCol(dbName=func.SUM(PhoneCall.q.minutes))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ViewPhoneMore2(ViewPhoneMore):
|
|
58
|
+
class sqlmeta:
|
|
59
|
+
table = 'vpm'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ViewPhoneInnerAggregate(ViewPhone):
|
|
63
|
+
twiceMinutes = IntCol(dbName=func.SUM(PhoneCall.q.minutes) * 2)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def setup_module(mod):
|
|
67
|
+
global calls, phones, sqlrepr
|
|
68
|
+
setupClass([PhoneNumber, PhoneCall])
|
|
69
|
+
ViewPhoneCall._connection = PhoneNumber._connection
|
|
70
|
+
ViewPhone._connection = PhoneNumber._connection
|
|
71
|
+
ViewPhoneMore._connection = PhoneNumber._connection
|
|
72
|
+
phones = inserts(PhoneNumber, [('1234567890',), ('1111111111',)], 'number')
|
|
73
|
+
calls = inserts(PhoneCall, [(phones[0], phones[1], 5),
|
|
74
|
+
(phones[0], phones[1], 20),
|
|
75
|
+
(phones[1], phones[0], 10),
|
|
76
|
+
(phones[1], phones[0], 25)],
|
|
77
|
+
'phoneNumber to minutes')
|
|
78
|
+
sqlrepr = PhoneNumber._connection.sqlrepr
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def testSimpleVPC():
|
|
82
|
+
assert hasattr(ViewPhoneCall, 'minutes')
|
|
83
|
+
assert hasattr(ViewPhoneCall, 'number')
|
|
84
|
+
assert hasattr(ViewPhoneCall, 'phoneNumberID')
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def testColumnSQLVPC():
|
|
88
|
+
assert str(sqlrepr(ViewPhoneCall.q.id)) == 'view_phone_call.id'
|
|
89
|
+
assert str(sqlrepr(ViewPhoneCall.q.minutes)) == 'view_phone_call.minutes'
|
|
90
|
+
q = sqlrepr(ViewPhoneCall.q)
|
|
91
|
+
assert q.count('phone_call.minutes AS minutes')
|
|
92
|
+
assert q.count('phone_number.number AS number')
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def testAliasOverride():
|
|
96
|
+
assert str(sqlrepr(ViewPhoneMore2.q.id)) == 'vpm.id'
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def checkAttr(cls, id, attr, value):
|
|
100
|
+
assert getattr(cls.get(id), attr) == value
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def testGetVPC():
|
|
104
|
+
checkAttr(ViewPhoneCall, calls[0].id, 'number',
|
|
105
|
+
calls[0].phoneNumber.number)
|
|
106
|
+
checkAttr(ViewPhoneCall, calls[0].id, 'minutes', calls[0].minutes)
|
|
107
|
+
checkAttr(ViewPhoneCall, calls[0].id, 'phoneNumber', calls[0].phoneNumber)
|
|
108
|
+
checkAttr(ViewPhoneCall, calls[2].id, 'number',
|
|
109
|
+
calls[2].phoneNumber.number)
|
|
110
|
+
checkAttr(ViewPhoneCall, calls[2].id, 'minutes', calls[2].minutes)
|
|
111
|
+
checkAttr(ViewPhoneCall, calls[2].id, 'phoneNumber', calls[2].phoneNumber)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def testGetVP():
|
|
115
|
+
checkAttr(ViewPhone, phones[0].id, 'number', phones[0].number)
|
|
116
|
+
checkAttr(ViewPhone, phones[0].id, 'minutes',
|
|
117
|
+
phones[0].calls.sum(PhoneCall.q.minutes))
|
|
118
|
+
checkAttr(ViewPhone, phones[0].id, 'phoneNumber', phones[0])
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def testGetVPM():
|
|
122
|
+
checkAttr(ViewPhoneMore, phones[0].id, 'number', phones[0].number)
|
|
123
|
+
checkAttr(ViewPhoneMore, phones[0].id, 'minutesCalled',
|
|
124
|
+
phones[0].incoming.sum(PhoneCall.q.minutes))
|
|
125
|
+
checkAttr(ViewPhoneMore, phones[0].id, 'timesCalled',
|
|
126
|
+
phones[0].incoming.count())
|
|
127
|
+
checkAttr(ViewPhoneMore, phones[0].id, 'timesCalledLong',
|
|
128
|
+
phones[0].incoming.filter(PhoneCall.q.minutes > 10).count())
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def testJoinView():
|
|
132
|
+
p = ViewPhone.get(phones[0].id)
|
|
133
|
+
assert p.calls.count() == 2
|
|
134
|
+
assert p.vCalls.count() == 2
|
|
135
|
+
assert p.vCalls[0] == ViewPhoneCall.get(calls[0].id)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def testInnerAggregate():
|
|
139
|
+
checkAttr(ViewPhoneInnerAggregate, phones[0].id, 'twiceMinutes',
|
|
140
|
+
phones[0].calls.sum(PhoneCall.q.minutes) * 2)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def testSelect():
|
|
144
|
+
s = ViewPhone.select()
|
|
145
|
+
assert s.count() == len(phones)
|
|
146
|
+
s = ViewPhoneCall.select()
|
|
147
|
+
assert s.count() == len(calls)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def testSelect2():
|
|
151
|
+
s = ViewPhone.select(ViewPhone.q.number == phones[0].number)
|
|
152
|
+
assert s.getOne().phoneNumber == phones[0]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def testDistinctCount():
|
|
156
|
+
# This test is for SelectResults non-* based count when distinct
|
|
157
|
+
# We're really just checking this doesn't raise anything
|
|
158
|
+
# due to lack of sqlrepr'ing.
|
|
159
|
+
assert ViewPhone.select(distinct=True).count() == 2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#
|