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,218 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from sqlobject import IntCol, LIKE, RLIKE, SQLObject, \
|
|
3
|
+
SQLObjectIntegrityError, SQLObjectNotFound, StringCol
|
|
4
|
+
from sqlobject.sqlbuilder import func
|
|
5
|
+
from .dbtest import raises, setupClass, supports, setSQLiteConnectionFactory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IterTest(SQLObject):
|
|
9
|
+
name = StringCol(dbName='name_col', length=200)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
names = ('a', 'b', 'c')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def setupIter():
|
|
16
|
+
setupClass(IterTest)
|
|
17
|
+
for n in names:
|
|
18
|
+
IterTest(name=n)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_00_normal():
|
|
22
|
+
setupIter()
|
|
23
|
+
count = 0
|
|
24
|
+
for test in IterTest.select():
|
|
25
|
+
count += 1
|
|
26
|
+
assert count == len(names)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_00b_lazy():
|
|
30
|
+
setupIter()
|
|
31
|
+
count = 0
|
|
32
|
+
for test in IterTest.select(lazyColumns=True):
|
|
33
|
+
count += 1
|
|
34
|
+
assert count == len(names)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_01_turn_to_list():
|
|
38
|
+
count = 0
|
|
39
|
+
for test in list(IterTest.select()):
|
|
40
|
+
count += 1
|
|
41
|
+
assert count == len(names)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_02_generator():
|
|
45
|
+
all = IterTest.select()
|
|
46
|
+
count = 0
|
|
47
|
+
for i, test in enumerate(all):
|
|
48
|
+
count += 1
|
|
49
|
+
assert count == len(names)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_03_ranged_indexed():
|
|
53
|
+
all = IterTest.select()
|
|
54
|
+
count = 0
|
|
55
|
+
for i in range(all.count()):
|
|
56
|
+
all[i] # test it's there
|
|
57
|
+
count += 1
|
|
58
|
+
assert count == len(names)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_04_indexed_ended_by_exception():
|
|
62
|
+
if not supports('limitSelect'):
|
|
63
|
+
pytest.skip("limitSelect isn't supported")
|
|
64
|
+
all = IterTest.select()
|
|
65
|
+
count = 0
|
|
66
|
+
try:
|
|
67
|
+
while 1:
|
|
68
|
+
all[count]
|
|
69
|
+
count += 1
|
|
70
|
+
# Stop the test if it's gone on too long
|
|
71
|
+
if count > len(names):
|
|
72
|
+
break
|
|
73
|
+
except IndexError:
|
|
74
|
+
pass
|
|
75
|
+
assert count == len(names)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_05_select_limit():
|
|
79
|
+
setupIter()
|
|
80
|
+
assert len(list(IterTest.select(limit=2))) == 2
|
|
81
|
+
raises(AssertionError, IterTest.select(limit=2).count)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_06_contains():
|
|
85
|
+
setupIter()
|
|
86
|
+
assert len(list(IterTest.select(IterTest.q.name.startswith('a')))) == 1
|
|
87
|
+
assert len(list(IterTest.select(IterTest.q.name.contains('a')))) == 1
|
|
88
|
+
assert len(list(IterTest.select(
|
|
89
|
+
IterTest.q.name.contains(func.lower('A'))))) == 1
|
|
90
|
+
assert len(list(IterTest.select(IterTest.q.name.contains("a'b")))) == 0
|
|
91
|
+
assert len(list(IterTest.select(IterTest.q.name.endswith('a')))) == 1
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_07_contains_special():
|
|
95
|
+
setupClass(IterTest)
|
|
96
|
+
a = IterTest(name='\\test')
|
|
97
|
+
b = IterTest(name='100%')
|
|
98
|
+
c = IterTest(name='test_')
|
|
99
|
+
assert list(IterTest.select(IterTest.q.name.startswith('\\'))) == [a]
|
|
100
|
+
assert list(IterTest.select(IterTest.q.name.contains('%'))) == [b]
|
|
101
|
+
assert list(IterTest.select(IterTest.q.name.endswith('_'))) == [c]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_select_getOne():
|
|
105
|
+
setupClass(IterTest)
|
|
106
|
+
a = IterTest(name='a')
|
|
107
|
+
b = IterTest(name='b')
|
|
108
|
+
assert IterTest.selectBy(name='a').getOne() == a
|
|
109
|
+
assert IterTest.select(IterTest.q.name == 'b').getOne() == b
|
|
110
|
+
assert IterTest.selectBy(name='c').getOne(None) is None
|
|
111
|
+
raises(SQLObjectNotFound, IterTest.selectBy(name="c").getOne)
|
|
112
|
+
IterTest(name='b')
|
|
113
|
+
raises(SQLObjectIntegrityError, IterTest.selectBy(name="b").getOne)
|
|
114
|
+
raises(SQLObjectIntegrityError, IterTest.selectBy(name="b").getOne, None)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_selectBy():
|
|
118
|
+
setupClass(IterTest)
|
|
119
|
+
IterTest(name='a')
|
|
120
|
+
IterTest(name='b')
|
|
121
|
+
assert IterTest.selectBy().count() == 2
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_selectBy_kwargs():
|
|
125
|
+
setupClass(IterTest)
|
|
126
|
+
raises(TypeError, IterTest, nonexistant='b')
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class UniqTest(SQLObject):
|
|
130
|
+
name = StringCol(dbName='name_col', unique=True, length=100)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_by_uniq():
|
|
134
|
+
setupClass(UniqTest)
|
|
135
|
+
a = UniqTest(name='a')
|
|
136
|
+
b = UniqTest(name='b')
|
|
137
|
+
assert UniqTest.byName('a') is a
|
|
138
|
+
assert UniqTest.byName('b') is b
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class Counter2(SQLObject):
|
|
142
|
+
|
|
143
|
+
n1 = IntCol(notNull=True)
|
|
144
|
+
n2 = IntCol(notNull=True)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class TestSelect:
|
|
148
|
+
|
|
149
|
+
def setup_method(self, meth):
|
|
150
|
+
setupClass(Counter2)
|
|
151
|
+
for i in range(10):
|
|
152
|
+
for j in range(10):
|
|
153
|
+
Counter2(n1=i, n2=j)
|
|
154
|
+
|
|
155
|
+
def counterEqual(self, counters, value):
|
|
156
|
+
assert [(c.n1, c.n2) for c in counters] == value
|
|
157
|
+
|
|
158
|
+
def accumulateEqual(self, func, counters, value):
|
|
159
|
+
assert func([c.n1 for c in counters]) == value
|
|
160
|
+
|
|
161
|
+
def test_1(self):
|
|
162
|
+
self.accumulateEqual(sum, Counter2.select(orderBy='n1'),
|
|
163
|
+
sum(range(10)) * 10)
|
|
164
|
+
|
|
165
|
+
def test_2(self):
|
|
166
|
+
self.accumulateEqual(len, Counter2.select('all'), 100)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_select_LIKE():
|
|
170
|
+
setupClass(IterTest)
|
|
171
|
+
IterTest(name='sqlobject')
|
|
172
|
+
IterTest(name='sqlbuilder')
|
|
173
|
+
assert IterTest.select(LIKE(IterTest.q.name, "sql%")).count() == 2
|
|
174
|
+
assert IterTest.select(LIKE(IterTest.q.name, "sqlb%")).count() == 1
|
|
175
|
+
assert IterTest.select(LIKE(IterTest.q.name, "sqlb%")).count() == 1
|
|
176
|
+
assert IterTest.select(LIKE(IterTest.q.name, "sqlx%")).count() == 0
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_select_RLIKE():
|
|
180
|
+
if not supports('rlike'):
|
|
181
|
+
pytest.skip("rlike isn't supported")
|
|
182
|
+
|
|
183
|
+
setupClass(IterTest)
|
|
184
|
+
|
|
185
|
+
if IterTest._connection.dbName == "sqlite":
|
|
186
|
+
# Implement regexp() function for SQLite
|
|
187
|
+
import re
|
|
188
|
+
|
|
189
|
+
def regexp(regexp, test):
|
|
190
|
+
return bool(re.search(regexp, test))
|
|
191
|
+
|
|
192
|
+
def SQLiteConnectionFactory(sqlite):
|
|
193
|
+
class MyConnection(sqlite.Connection):
|
|
194
|
+
def __init__(self, *args, **kwargs):
|
|
195
|
+
super(MyConnection, self).__init__(*args, **kwargs)
|
|
196
|
+
self.create_function("regexp", 2, regexp)
|
|
197
|
+
return MyConnection
|
|
198
|
+
|
|
199
|
+
setSQLiteConnectionFactory(IterTest, SQLiteConnectionFactory)
|
|
200
|
+
|
|
201
|
+
IterTest(name='sqlobject')
|
|
202
|
+
IterTest(name='sqlbuilder')
|
|
203
|
+
assert IterTest.select(RLIKE(IterTest.q.name, "^sql.*$")).count() == 2
|
|
204
|
+
assert IterTest.select(RLIKE(IterTest.q.name, "^sqlb.*$")).count() == 1
|
|
205
|
+
assert IterTest.select(RLIKE(IterTest.q.name, "^sqlb.*$")).count() == 1
|
|
206
|
+
assert IterTest.select(RLIKE(IterTest.q.name, "^sqlx.*$")).count() == 0
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_select_sqlbuilder():
|
|
210
|
+
setupClass(IterTest)
|
|
211
|
+
IterTest(name='sqlobject')
|
|
212
|
+
IterTest.select(IterTest.q.name == u'sqlobject')
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_select_perConnection():
|
|
216
|
+
setupClass(IterTest)
|
|
217
|
+
IterTest(name='a')
|
|
218
|
+
assert not IterTest.select().getOne().sqlmeta._perConnection
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from sqlobject import ForeignKey, SQLMultipleJoin, SQLObject, SQLRelatedJoin, \
|
|
3
|
+
StringCol
|
|
4
|
+
from sqlobject.tests.dbtest import inserts, setupClass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Tests retrieving objects through a join/fk on a selectResults
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SRThrough1(SQLObject):
|
|
11
|
+
three = ForeignKey('SRThrough3')
|
|
12
|
+
twos = SQLMultipleJoin('SRThrough2', joinColumn='oneID')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SRThrough2(SQLObject):
|
|
16
|
+
one = ForeignKey('SRThrough1')
|
|
17
|
+
threes = SQLRelatedJoin('SRThrough3', addRemoveName='Three')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SRThrough3(SQLObject):
|
|
21
|
+
name = StringCol()
|
|
22
|
+
ones = SQLMultipleJoin('SRThrough1', joinColumn='threeID')
|
|
23
|
+
twos = SQLRelatedJoin('SRThrough2')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def setup_module(mod):
|
|
27
|
+
global ones, twos, threes
|
|
28
|
+
setupClass([SRThrough3, SRThrough1, SRThrough2])
|
|
29
|
+
threes = inserts(SRThrough3,
|
|
30
|
+
[('a',), ('b',), ('c',)],
|
|
31
|
+
'name')
|
|
32
|
+
ones = inserts(SRThrough1,
|
|
33
|
+
[(threes[0].id,), (threes[0].id,), (threes[2].id,)],
|
|
34
|
+
'threeID')
|
|
35
|
+
twos = inserts(SRThrough2,
|
|
36
|
+
[(ones[0].id,), (ones[1].id,), (ones[2].id,)],
|
|
37
|
+
'oneID')
|
|
38
|
+
twos[0].addThree(threes[0])
|
|
39
|
+
twos[0].addThree(threes[1])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def testBadRef():
|
|
43
|
+
with pytest.raises(AttributeError):
|
|
44
|
+
threes[0].throughTo.four
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def testThroughFK():
|
|
48
|
+
assert list(threes[0].ones.throughTo.three) == [threes[0]]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def testThroughMultipleJoin():
|
|
52
|
+
assert list(threes[0].ones.throughTo.twos) == [twos[0], twos[1]]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def testThroughRelatedJoin():
|
|
56
|
+
assert list(threes[0].twos.throughTo.threes) == [threes[0], threes[1]]
|
|
57
|
+
assert list(
|
|
58
|
+
SRThrough3.select(SRThrough3.q.id == threes[0].id).throughTo.twos) == \
|
|
59
|
+
list(threes[0].twos)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def testThroughFKAndJoin():
|
|
63
|
+
assert list(threes[0].ones.throughTo.three.throughTo.twos) == [twos[0]]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from sqlobject import SQLObject, StringCol
|
|
2
|
+
from sqlobject.tests.dbtest import setupClass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SOTestSetters(SQLObject):
|
|
6
|
+
firstName = StringCol(length=50, dbName='fname_col', default=None)
|
|
7
|
+
lastName = StringCol(length=50, dbName='lname_col', default=None)
|
|
8
|
+
|
|
9
|
+
def _set_name(self, v):
|
|
10
|
+
firstName, lastName = v.split()
|
|
11
|
+
self.firstName = firstName
|
|
12
|
+
self.lastName = lastName
|
|
13
|
+
|
|
14
|
+
def _get_name(self):
|
|
15
|
+
return "%s %s" % (self.firstName, self.lastName)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_create():
|
|
19
|
+
setupClass(SOTestSetters)
|
|
20
|
+
t = SOTestSetters(name='John Doe')
|
|
21
|
+
assert t.firstName == 'John'
|
|
22
|
+
assert t.lastName == 'Doe'
|
|
23
|
+
assert t.name == 'John Doe'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from sqlobject import IntCol, SQLObject
|
|
3
|
+
from sqlobject.tests.dbtest import setupClass, supports
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
########################################
|
|
7
|
+
# Slicing tests
|
|
8
|
+
########################################
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def listrange(*args):
|
|
12
|
+
"""Always return a list, for py3k compatibility"""
|
|
13
|
+
return list(range(*args))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Counter(SQLObject):
|
|
17
|
+
|
|
18
|
+
number = IntCol(notNull=True)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestSlice:
|
|
22
|
+
|
|
23
|
+
def setup_method(self, meth):
|
|
24
|
+
setupClass(Counter)
|
|
25
|
+
for i in range(100):
|
|
26
|
+
Counter(number=i)
|
|
27
|
+
|
|
28
|
+
def counterEqual(self, counters, value):
|
|
29
|
+
if not supports('limitSelect'):
|
|
30
|
+
pytest.skip("limitSelect isn't supported")
|
|
31
|
+
assert [c.number for c in counters] == value
|
|
32
|
+
|
|
33
|
+
def test_slice(self):
|
|
34
|
+
self.counterEqual(
|
|
35
|
+
Counter.select(None, orderBy='number'), listrange(100))
|
|
36
|
+
|
|
37
|
+
self.counterEqual(
|
|
38
|
+
Counter.select(None, orderBy='number')[10:20],
|
|
39
|
+
listrange(10, 20))
|
|
40
|
+
|
|
41
|
+
self.counterEqual(
|
|
42
|
+
Counter.select(None, orderBy='number')[20:30][:5],
|
|
43
|
+
listrange(20, 25))
|
|
44
|
+
|
|
45
|
+
self.counterEqual(
|
|
46
|
+
Counter.select(None, orderBy='number')[20:30][1:5],
|
|
47
|
+
listrange(21, 25))
|
|
48
|
+
|
|
49
|
+
self.counterEqual(
|
|
50
|
+
Counter.select(None, orderBy='number')[:-10],
|
|
51
|
+
listrange(0, 90))
|
|
52
|
+
|
|
53
|
+
self.counterEqual(
|
|
54
|
+
Counter.select(None, orderBy='number', reversed=True),
|
|
55
|
+
listrange(99, -1, -1))
|
|
56
|
+
|
|
57
|
+
self.counterEqual(
|
|
58
|
+
Counter.select(None, orderBy='-number'),
|
|
59
|
+
listrange(99, -1, -1))
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from sqlobject import DESC, SQLObject, StringCol, sqlmeta
|
|
2
|
+
from sqlobject.tests.dbtest import inserts, setupClass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Names(SQLObject):
|
|
6
|
+
|
|
7
|
+
class sqlmeta(sqlmeta):
|
|
8
|
+
table = 'names_table'
|
|
9
|
+
defaultOrder = ['lastName', 'firstName']
|
|
10
|
+
|
|
11
|
+
firstName = StringCol(length=30)
|
|
12
|
+
lastName = StringCol(length=30)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def setupNames():
|
|
16
|
+
setupClass(Names)
|
|
17
|
+
inserts(Names, [('aj', 'baker'), ('joe', 'robbins'),
|
|
18
|
+
('tim', 'jackson'), ('joe', 'baker'),
|
|
19
|
+
('zoe', 'robbins')],
|
|
20
|
+
schema='firstName lastName')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def nameList(names):
|
|
24
|
+
result = []
|
|
25
|
+
for name in names:
|
|
26
|
+
result.append('%s %s' % (name.firstName, name.lastName))
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def firstList(names):
|
|
31
|
+
return [n.firstName for n in names]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_defaultOrder():
|
|
35
|
+
setupNames()
|
|
36
|
+
assert nameList(Names.select()) == \
|
|
37
|
+
['aj baker', 'joe baker',
|
|
38
|
+
'tim jackson', 'joe robbins',
|
|
39
|
+
'zoe robbins']
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_otherOrder():
|
|
43
|
+
setupNames()
|
|
44
|
+
assert nameList(Names.select().orderBy(['firstName', 'lastName'])) == \
|
|
45
|
+
['aj baker', 'joe baker',
|
|
46
|
+
'joe robbins', 'tim jackson',
|
|
47
|
+
'zoe robbins']
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_untranslatedColumnOrder():
|
|
51
|
+
setupNames()
|
|
52
|
+
assert nameList(Names.select().orderBy(['first_name', 'last_name'])) == \
|
|
53
|
+
['aj baker', 'joe baker',
|
|
54
|
+
'joe robbins', 'tim jackson',
|
|
55
|
+
'zoe robbins']
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_singleUntranslatedColumnOrder():
|
|
59
|
+
setupNames()
|
|
60
|
+
assert firstList(Names.select().orderBy('firstName')) == \
|
|
61
|
+
['aj', 'joe', 'joe', 'tim', 'zoe']
|
|
62
|
+
assert firstList(Names.select().orderBy('first_name')) == \
|
|
63
|
+
['aj', 'joe', 'joe', 'tim', 'zoe']
|
|
64
|
+
assert firstList(Names.select().orderBy('-firstName')) == \
|
|
65
|
+
['zoe', 'tim', 'joe', 'joe', 'aj']
|
|
66
|
+
assert firstList(Names.select().orderBy(u'-first_name')) == \
|
|
67
|
+
['zoe', 'tim', 'joe', 'joe', 'aj']
|
|
68
|
+
assert firstList(Names.select().orderBy(Names.q.firstName)) == \
|
|
69
|
+
['aj', 'joe', 'joe', 'tim', 'zoe']
|
|
70
|
+
assert firstList(Names.select().orderBy('firstName').reversed()) == \
|
|
71
|
+
['zoe', 'tim', 'joe', 'joe', 'aj']
|
|
72
|
+
assert firstList(Names.select().orderBy('-firstName').reversed()) == \
|
|
73
|
+
['aj', 'joe', 'joe', 'tim', 'zoe']
|
|
74
|
+
assert firstList(Names.select().orderBy(DESC(Names.q.firstName))) == \
|
|
75
|
+
['zoe', 'tim', 'joe', 'joe', 'aj']
|
|
76
|
+
assert firstList(Names.select().orderBy(Names.q.firstName).reversed()) == \
|
|
77
|
+
['zoe', 'tim', 'joe', 'joe', 'aj']
|
|
78
|
+
assert firstList(
|
|
79
|
+
Names.select().orderBy(DESC(Names.q.firstName)).reversed()) == \
|
|
80
|
+
['aj', 'joe', 'joe', 'tim', 'zoe']
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from sqlobject import IntCol, SQLObject, StringCol
|
|
2
|
+
from sqlobject.compat import PY2
|
|
3
|
+
from sqlobject.sqlbuilder import AND, ANY, CONCAT, Delete, Insert, \
|
|
4
|
+
SQLOp, Select, Union, Update, const, func, sqlrepr
|
|
5
|
+
from sqlobject.tests.dbtest import getConnection, raises, setupClass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SOTestSQLBuilder(SQLObject):
|
|
9
|
+
name = StringCol()
|
|
10
|
+
so_value = IntCol()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_Select():
|
|
14
|
+
setupClass(SOTestSQLBuilder)
|
|
15
|
+
|
|
16
|
+
select1 = Select([const.id, func.MAX(const.salary)],
|
|
17
|
+
staticTables=['employees'])
|
|
18
|
+
assert sqlrepr(select1) == 'SELECT id, MAX(salary) FROM employees'
|
|
19
|
+
|
|
20
|
+
select2 = Select([SOTestSQLBuilder.q.name, SOTestSQLBuilder.q.so_value])
|
|
21
|
+
assert sqlrepr(select2) == \
|
|
22
|
+
'SELECT so_test_sql_builder.name, so_test_sql_builder.so_value ' \
|
|
23
|
+
'FROM so_test_sql_builder'
|
|
24
|
+
|
|
25
|
+
union = Union(select1, select2)
|
|
26
|
+
assert sqlrepr(union) == \
|
|
27
|
+
'SELECT id, MAX(salary) FROM employees ' \
|
|
28
|
+
'UNION SELECT so_test_sql_builder.name, ' \
|
|
29
|
+
'so_test_sql_builder.so_value ' \
|
|
30
|
+
'FROM so_test_sql_builder'
|
|
31
|
+
|
|
32
|
+
union = Union(SOTestSQLBuilder.select().queryForSelect())
|
|
33
|
+
assert sqlrepr(union) == \
|
|
34
|
+
'SELECT so_test_sql_builder.id, so_test_sql_builder.name, ' \
|
|
35
|
+
'so_test_sql_builder.so_value FROM so_test_sql_builder WHERE 1 = 1'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_empty_AND():
|
|
39
|
+
assert AND() is None
|
|
40
|
+
assert AND(True) is True
|
|
41
|
+
|
|
42
|
+
# sqlrepr() is needed because AND() returns an SQLExpression that overrides
|
|
43
|
+
# comparison. The following
|
|
44
|
+
# AND('x', 'y') == "foo bar"
|
|
45
|
+
# is True! (-: Eeek!
|
|
46
|
+
assert sqlrepr(AND(1, 2)) == sqlrepr(SQLOp("AND", 1, 2)) == "((1) AND (2))"
|
|
47
|
+
assert sqlrepr(AND(1, 2, '3'), "sqlite") == \
|
|
48
|
+
sqlrepr(SQLOp("AND", 1, SQLOp("AND", 2, '3')), "sqlite") == \
|
|
49
|
+
"((1) AND ((2) AND ('3')))"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_modulo():
|
|
53
|
+
setupClass(SOTestSQLBuilder)
|
|
54
|
+
assert sqlrepr(SOTestSQLBuilder.q.so_value % 2 == 0, 'mysql') == \
|
|
55
|
+
"((MOD(so_test_sql_builder.so_value, 2)) = (0))"
|
|
56
|
+
assert sqlrepr(SOTestSQLBuilder.q.so_value % 2 == 0, 'sqlite') == \
|
|
57
|
+
"(((so_test_sql_builder.so_value) % (2)) = (0))"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_division():
|
|
61
|
+
SOTestSQLBuilder(name='test', so_value=-11)
|
|
62
|
+
assert sqlrepr(SOTestSQLBuilder.q.so_value / 4 == -2.75, 'mysql') == \
|
|
63
|
+
"(((so_test_sql_builder.so_value) / (4)) = (-2.75))"
|
|
64
|
+
assert sqlrepr(SOTestSQLBuilder.q.so_value // 4 == -3, 'mysql') == \
|
|
65
|
+
"((FLOOR(((so_test_sql_builder.so_value) / (4)))) = (-3))"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_str_or_sqlrepr():
|
|
69
|
+
select = Select(['id', 'name'], staticTables=['employees'],
|
|
70
|
+
where='value>0', orderBy='id')
|
|
71
|
+
assert sqlrepr(select, 'sqlite') == \
|
|
72
|
+
'SELECT id, name FROM employees WHERE value>0 ORDER BY id'
|
|
73
|
+
|
|
74
|
+
select = Select(['id', 'name'], staticTables=['employees'],
|
|
75
|
+
where='value>0', orderBy='id', lazyColumns=True)
|
|
76
|
+
assert sqlrepr(select, 'sqlite') == \
|
|
77
|
+
'SELECT id FROM employees WHERE value>0 ORDER BY id'
|
|
78
|
+
|
|
79
|
+
insert = Insert('employees', values={'id': 1, 'name': 'test'})
|
|
80
|
+
assert sqlrepr(insert, 'sqlite') == \
|
|
81
|
+
"INSERT INTO employees (id, name) VALUES (1, 'test')"
|
|
82
|
+
|
|
83
|
+
update = Update('employees', {'name': 'test'}, where='id=1')
|
|
84
|
+
assert sqlrepr(update, 'sqlite') == \
|
|
85
|
+
"UPDATE employees SET name='test' WHERE id=1"
|
|
86
|
+
|
|
87
|
+
update = Update('employees', {'name': 'test', 'age': 42}, where='id=1')
|
|
88
|
+
assert sqlrepr(update, 'sqlite') == \
|
|
89
|
+
"UPDATE employees SET age=42, name='test' WHERE id=1"
|
|
90
|
+
|
|
91
|
+
delete = Delete('employees', where='id=1')
|
|
92
|
+
assert sqlrepr(delete, 'sqlite') == \
|
|
93
|
+
"DELETE FROM employees WHERE id=1"
|
|
94
|
+
|
|
95
|
+
raises(TypeError, Delete, 'employees')
|
|
96
|
+
|
|
97
|
+
delete = Delete('employees', where=None)
|
|
98
|
+
assert sqlrepr(delete, 'sqlite') == \
|
|
99
|
+
"DELETE FROM employees"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_CONCAT():
|
|
103
|
+
setupClass(SOTestSQLBuilder)
|
|
104
|
+
SOTestSQLBuilder(name='test', so_value=42)
|
|
105
|
+
|
|
106
|
+
assert sqlrepr(CONCAT('a', 'b'), 'mysql') == "CONCAT('a', 'b')"
|
|
107
|
+
assert sqlrepr(CONCAT('a', 'b'), 'mssql') == "'a' + 'b'"
|
|
108
|
+
assert sqlrepr(CONCAT('a', 'b'), 'sqlite') == "'a' || 'b'"
|
|
109
|
+
assert sqlrepr(CONCAT('prefix', SOTestSQLBuilder.q.name), 'mysql') == \
|
|
110
|
+
"CONCAT('prefix', so_test_sql_builder.name)"
|
|
111
|
+
assert sqlrepr(CONCAT('prefix', SOTestSQLBuilder.q.name), 'sqlite') == \
|
|
112
|
+
"'prefix' || so_test_sql_builder.name"
|
|
113
|
+
|
|
114
|
+
select = Select([CONCAT(SOTestSQLBuilder.q.name, '-suffix')],
|
|
115
|
+
staticTables=['so_test_sql_builder'])
|
|
116
|
+
connection = getConnection()
|
|
117
|
+
rows = connection.queryAll(connection.sqlrepr(select))
|
|
118
|
+
result = rows[0][0]
|
|
119
|
+
if not PY2 and not isinstance(result, str):
|
|
120
|
+
result = result.decode('ascii')
|
|
121
|
+
assert result == "test-suffix"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_ANY():
|
|
125
|
+
setupClass(SOTestSQLBuilder)
|
|
126
|
+
|
|
127
|
+
select = Select(
|
|
128
|
+
[SOTestSQLBuilder.q.name],
|
|
129
|
+
'value' == ANY(SOTestSQLBuilder.q.so_value),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
assert sqlrepr(select, 'mysql') == \
|
|
133
|
+
"SELECT so_test_sql_builder.name FROM so_test_sql_builder " \
|
|
134
|
+
"WHERE (('value') = ANY (so_test_sql_builder.so_value))"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
|
|
3
|
+
from sqlobject import BoolCol, SQLObject
|
|
4
|
+
from sqlobject.sqlbuilder import AND, Alias, EXISTS, JOIN, LEFTJOINOn, \
|
|
5
|
+
Select, sqlrepr
|
|
6
|
+
from sqlobject.tests.dbtest import setupClass
|
|
7
|
+
|
|
8
|
+
''' Going to test that complex sqlbuilder constructions are never
|
|
9
|
+
prematurely stringified. A straight-forward approach is to use
|
|
10
|
+
Bools, since postgresql wants special formatting in queries.
|
|
11
|
+
The test is whether a call to sqlrepr(x, 'postgres') includes
|
|
12
|
+
the appropriate bool formatting throughout.
|
|
13
|
+
'''
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SBButton(SQLObject):
|
|
17
|
+
activated = BoolCol()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def makeClause():
|
|
21
|
+
# It's not a comparison, it's an SQLExpression
|
|
22
|
+
return SBButton.q.activated == True # noqa
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def makeSelect():
|
|
26
|
+
return Select(SBButton.q.id, clause=makeClause())
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def checkCount(q, c, msg=''):
|
|
30
|
+
print("STRING:", str(q))
|
|
31
|
+
print("POSTGR:", sqlrepr(q, 'postgres'))
|
|
32
|
+
assert sqlrepr(q, 'postgres').count("'t'") == c and \
|
|
33
|
+
sqlrepr(q, 'postgres') != str(q), msg
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def testSimple():
|
|
37
|
+
setupClass(SBButton)
|
|
38
|
+
checkCount(makeClause(), 1)
|
|
39
|
+
checkCount(makeSelect(), 1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def testMiscOps():
|
|
43
|
+
setupClass(SBButton)
|
|
44
|
+
checkCount(AND(makeClause(), makeClause()), 2)
|
|
45
|
+
checkCount(AND(makeClause(), EXISTS(makeSelect())), 2)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def testAliased():
|
|
49
|
+
setupClass(SBButton)
|
|
50
|
+
b = Alias(makeSelect(), 'b')
|
|
51
|
+
checkCount(b, 1)
|
|
52
|
+
checkCount(Select(b.q.id), 1)
|
|
53
|
+
|
|
54
|
+
# Table1 & Table2 are treated individually in joins
|
|
55
|
+
checkCount(JOIN(None, b), 1)
|
|
56
|
+
checkCount(JOIN(b, SBButton), 1)
|
|
57
|
+
checkCount(JOIN(SBButton, b), 1)
|
|
58
|
+
checkCount(LEFTJOINOn(None, b, SBButton.q.id == b.q.id), 1)
|
|
59
|
+
checkCount(LEFTJOINOn(b, SBButton, SBButton.q.id == b.q.id), 1)
|
|
60
|
+
checkCount(LEFTJOINOn(SBButton, b, SBButton.q.id == b.q.id), 1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def testTablesUsedSResults():
|
|
64
|
+
setupClass(SBButton)
|
|
65
|
+
|
|
66
|
+
checkCount(SBButton.select(makeClause()).queryForSelect(), 1)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from sqlobject import SQLObject, StringCol
|
|
2
|
+
from sqlobject.sqlbuilder import Alias, ImportProxy, tablesUsedSet
|
|
3
|
+
from sqlobject.views import ViewSQLObject
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def testSimple():
|
|
7
|
+
nyi = ImportProxy('NotYetImported')
|
|
8
|
+
x = nyi.q.name
|
|
9
|
+
|
|
10
|
+
class NotYetImported(SQLObject):
|
|
11
|
+
name = StringCol(dbName='a_name')
|
|
12
|
+
|
|
13
|
+
y = nyi.q.name
|
|
14
|
+
|
|
15
|
+
assert str(x) == 'not_yet_imported.a_name'
|
|
16
|
+
assert str(y) == 'not_yet_imported.a_name'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def testAddition():
|
|
20
|
+
nyi = ImportProxy('NotYetImported2')
|
|
21
|
+
x = nyi.q.name + nyi.q.name
|
|
22
|
+
|
|
23
|
+
class NotYetImported2(SQLObject):
|
|
24
|
+
name = StringCol(dbName='a_name')
|
|
25
|
+
|
|
26
|
+
assert str(x) == \
|
|
27
|
+
'((not_yet_imported2.a_name) + (not_yet_imported2.a_name))'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def testOnView():
|
|
31
|
+
nyi = ImportProxy('NotYetImportedV')
|
|
32
|
+
x = nyi.q.name
|
|
33
|
+
|
|
34
|
+
class NotYetImported3(SQLObject):
|
|
35
|
+
name = StringCol(dbName='a_name')
|
|
36
|
+
|
|
37
|
+
class NotYetImportedV(ViewSQLObject):
|
|
38
|
+
class sqlmeta:
|
|
39
|
+
idName = NotYetImported3.q.id
|
|
40
|
+
name = StringCol(dbName=NotYetImported3.q.name)
|
|
41
|
+
|
|
42
|
+
assert str(x) == 'not_yet_imported_v.name'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def testAlias():
|
|
46
|
+
nyi = ImportProxy('NotYetImported4')
|
|
47
|
+
y = Alias(nyi, 'y')
|
|
48
|
+
x = y.q.name
|
|
49
|
+
|
|
50
|
+
class NotYetImported4(SQLObject):
|
|
51
|
+
name = StringCol(dbName='a_name')
|
|
52
|
+
|
|
53
|
+
assert str(y) == 'not_yet_imported4 y'
|
|
54
|
+
assert tablesUsedSet(x, None) == set(['not_yet_imported4 y'])
|
|
55
|
+
assert str(x) == 'y.a_name'
|