piccolo 1.21.0__py3-none-any.whl → 1.23.0__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.
- piccolo/__init__.py +1 -1
- piccolo/apps/asgi/commands/new.py +3 -0
- piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja +60 -0
- piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja +119 -0
- piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja +121 -0
- piccolo/apps/asgi/commands/templates/app/app.py.jinja +6 -0
- piccolo/apps/asgi/commands/templates/app/home/_falcon_endpoints.py.jinja +19 -0
- piccolo/apps/asgi/commands/templates/app/home/_quart_endpoints.py.jinja +18 -0
- piccolo/apps/asgi/commands/templates/app/home/_sanic_endpoints.py.jinja +17 -0
- piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja +6 -0
- piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw +15 -0
- piccolo/apps/playground/commands/run.py +9 -0
- piccolo/columns/column_types.py +78 -39
- piccolo/columns/defaults/timestamptz.py +1 -0
- piccolo/engine/sqlite.py +14 -2
- piccolo/query/base.py +11 -6
- piccolo/query/operators/__init__.py +0 -0
- piccolo/query/operators/json.py +111 -0
- piccolo/querystring.py +14 -2
- piccolo/table_reflection.py +17 -5
- {piccolo-1.21.0.dist-info → piccolo-1.23.0.dist-info}/METADATA +34 -22
- {piccolo-1.21.0.dist-info → piccolo-1.23.0.dist-info}/RECORD +31 -20
- {piccolo-1.21.0.dist-info → piccolo-1.23.0.dist-info}/WHEEL +1 -1
- tests/columns/test_integer.py +32 -0
- tests/columns/test_jsonb.py +100 -43
- tests/query/operators/__init__.py +0 -0
- tests/query/operators/test_json.py +52 -0
- tests/table/test_insert.py +1 -1
- {piccolo-1.21.0.dist-info → piccolo-1.23.0.dist-info}/LICENSE +0 -0
- {piccolo-1.21.0.dist-info → piccolo-1.23.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.21.0.dist-info → piccolo-1.23.0.dist-info}/top_level.txt +0 -0
tests/columns/test_jsonb.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from piccolo.columns.column_types import JSONB, ForeignKey, Varchar
|
2
2
|
from piccolo.table import Table
|
3
|
-
from piccolo.testing.test_case import TableTest
|
3
|
+
from piccolo.testing.test_case import AsyncTableTest, TableTest
|
4
4
|
from tests.base import engines_only, engines_skip
|
5
5
|
|
6
6
|
|
@@ -137,93 +137,150 @@ class TestJSONB(TableTest):
|
|
137
137
|
[{"name": "Guitar", "studio_facilities": {"mixing_desk": True}}],
|
138
138
|
)
|
139
139
|
|
140
|
-
|
140
|
+
|
141
|
+
@engines_only("postgres", "cockroach")
|
142
|
+
class TestArrow(AsyncTableTest):
|
143
|
+
tables = [RecordingStudio, Instrument]
|
144
|
+
|
145
|
+
async def insert_row(self):
|
146
|
+
await RecordingStudio(
|
147
|
+
name="Abbey Road", facilities='{"mixing_desk": true}'
|
148
|
+
).save()
|
149
|
+
|
150
|
+
async def test_arrow(self):
|
141
151
|
"""
|
142
152
|
Test using the arrow function to retrieve a subset of the JSON.
|
143
153
|
"""
|
144
|
-
|
145
|
-
name="Abbey Road", facilities='{"mixing_desk": true}'
|
146
|
-
).save().run_sync()
|
154
|
+
await self.insert_row()
|
147
155
|
|
148
|
-
row = (
|
149
|
-
RecordingStudio.
|
150
|
-
|
151
|
-
)
|
152
|
-
.first()
|
153
|
-
.run_sync()
|
154
|
-
)
|
156
|
+
row = await RecordingStudio.select(
|
157
|
+
RecordingStudio.facilities.arrow("mixing_desk")
|
158
|
+
).first()
|
155
159
|
assert row is not None
|
156
160
|
self.assertEqual(row["facilities"], "true")
|
157
161
|
|
158
|
-
row = (
|
162
|
+
row = await (
|
159
163
|
RecordingStudio.select(
|
160
164
|
RecordingStudio.facilities.arrow("mixing_desk")
|
161
165
|
)
|
162
166
|
.output(load_json=True)
|
163
167
|
.first()
|
164
|
-
.run_sync()
|
165
168
|
)
|
166
169
|
assert row is not None
|
167
170
|
self.assertEqual(row["facilities"], True)
|
168
171
|
|
169
|
-
def test_arrow_as_alias(self):
|
172
|
+
async def test_arrow_as_alias(self):
|
170
173
|
"""
|
171
174
|
Test using the arrow function to retrieve a subset of the JSON.
|
172
175
|
"""
|
173
|
-
|
174
|
-
name="Abbey Road", facilities='{"mixing_desk": true}'
|
175
|
-
).save().run_sync()
|
176
|
+
await self.insert_row()
|
176
177
|
|
177
|
-
row = (
|
178
|
-
RecordingStudio.
|
179
|
-
|
180
|
-
"mixing_desk"
|
181
|
-
)
|
178
|
+
row = await RecordingStudio.select(
|
179
|
+
RecordingStudio.facilities.arrow("mixing_desk").as_alias(
|
180
|
+
"mixing_desk"
|
182
181
|
)
|
183
|
-
|
184
|
-
|
185
|
-
)
|
182
|
+
).first()
|
183
|
+
assert row is not None
|
184
|
+
self.assertEqual(row["mixing_desk"], "true")
|
185
|
+
|
186
|
+
async def test_square_brackets(self):
|
187
|
+
"""
|
188
|
+
Make sure we can use square brackets instead of calling ``arrow``
|
189
|
+
explicitly.
|
190
|
+
"""
|
191
|
+
await self.insert_row()
|
192
|
+
|
193
|
+
row = await RecordingStudio.select(
|
194
|
+
RecordingStudio.facilities["mixing_desk"].as_alias("mixing_desk")
|
195
|
+
).first()
|
186
196
|
assert row is not None
|
187
197
|
self.assertEqual(row["mixing_desk"], "true")
|
188
198
|
|
189
|
-
def
|
199
|
+
async def test_multiple_levels_deep(self):
|
200
|
+
"""
|
201
|
+
Make sure elements can be extracted multiple levels deep, and using
|
202
|
+
array indexes.
|
203
|
+
"""
|
204
|
+
await RecordingStudio(
|
205
|
+
name="Abbey Road",
|
206
|
+
facilities={
|
207
|
+
"technicians": [
|
208
|
+
{"name": "Alice Jones"},
|
209
|
+
{"name": "Bob Williams"},
|
210
|
+
]
|
211
|
+
},
|
212
|
+
).save()
|
213
|
+
|
214
|
+
response = await RecordingStudio.select(
|
215
|
+
RecordingStudio.facilities["technicians"][0]["name"].as_alias(
|
216
|
+
"technician_name"
|
217
|
+
)
|
218
|
+
).output(load_json=True)
|
219
|
+
assert response is not None
|
220
|
+
self.assertListEqual(response, [{"technician_name": "Alice Jones"}])
|
221
|
+
|
222
|
+
async def test_arrow_where(self):
|
190
223
|
"""
|
191
224
|
Make sure the arrow function can be used within a WHERE clause.
|
192
225
|
"""
|
193
|
-
|
194
|
-
name="Abbey Road", facilities='{"mixing_desk": true}'
|
195
|
-
).save().run_sync()
|
226
|
+
await self.insert_row()
|
196
227
|
|
197
228
|
self.assertEqual(
|
198
|
-
RecordingStudio.count()
|
199
|
-
|
200
|
-
|
229
|
+
await RecordingStudio.count().where(
|
230
|
+
RecordingStudio.facilities.arrow("mixing_desk").eq(True)
|
231
|
+
),
|
201
232
|
1,
|
202
233
|
)
|
203
234
|
|
204
235
|
self.assertEqual(
|
205
|
-
RecordingStudio.count()
|
206
|
-
|
207
|
-
|
236
|
+
await RecordingStudio.count().where(
|
237
|
+
RecordingStudio.facilities.arrow("mixing_desk").eq(False)
|
238
|
+
),
|
208
239
|
0,
|
209
240
|
)
|
210
241
|
|
211
|
-
def test_arrow_first(self):
|
242
|
+
async def test_arrow_first(self):
|
212
243
|
"""
|
213
244
|
Make sure the arrow function can be used with the first clause.
|
214
245
|
"""
|
215
|
-
RecordingStudio.insert(
|
246
|
+
await RecordingStudio.insert(
|
216
247
|
RecordingStudio(facilities='{"mixing_desk": true}'),
|
217
248
|
RecordingStudio(facilities='{"mixing_desk": false}'),
|
218
|
-
)
|
249
|
+
)
|
219
250
|
|
220
251
|
self.assertEqual(
|
221
|
-
RecordingStudio.select(
|
252
|
+
await RecordingStudio.select(
|
222
253
|
RecordingStudio.facilities.arrow("mixing_desk").as_alias(
|
223
254
|
"mixing_desk"
|
224
255
|
)
|
225
|
-
)
|
226
|
-
.first()
|
227
|
-
.run_sync(),
|
256
|
+
).first(),
|
228
257
|
{"mixing_desk": "true"},
|
229
258
|
)
|
259
|
+
|
260
|
+
|
261
|
+
@engines_only("postgres", "cockroach")
|
262
|
+
class TestFromPath(AsyncTableTest):
|
263
|
+
|
264
|
+
tables = [RecordingStudio, Instrument]
|
265
|
+
|
266
|
+
async def test_from_path(self):
|
267
|
+
"""
|
268
|
+
Make sure ``from_path`` can be used for complex nested data.
|
269
|
+
"""
|
270
|
+
await RecordingStudio(
|
271
|
+
name="Abbey Road",
|
272
|
+
facilities={
|
273
|
+
"technicians": [
|
274
|
+
{"name": "Alice Jones"},
|
275
|
+
{"name": "Bob Williams"},
|
276
|
+
]
|
277
|
+
},
|
278
|
+
).save()
|
279
|
+
|
280
|
+
response = await RecordingStudio.select(
|
281
|
+
RecordingStudio.facilities.from_path(
|
282
|
+
["technicians", 0, "name"]
|
283
|
+
).as_alias("technician_name")
|
284
|
+
).output(load_json=True)
|
285
|
+
assert response is not None
|
286
|
+
self.assertListEqual(response, [{"technician_name": "Alice Jones"}])
|
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from unittest import TestCase
|
2
|
+
|
3
|
+
from piccolo.columns import JSONB
|
4
|
+
from piccolo.query.operators.json import GetChildElement, GetElementFromPath
|
5
|
+
from piccolo.table import Table
|
6
|
+
from tests.base import engines_skip
|
7
|
+
|
8
|
+
|
9
|
+
class RecordingStudio(Table):
|
10
|
+
facilities = JSONB(null=True)
|
11
|
+
|
12
|
+
|
13
|
+
@engines_skip("sqlite")
|
14
|
+
class TestGetChildElement(TestCase):
|
15
|
+
|
16
|
+
def test_query(self):
|
17
|
+
"""
|
18
|
+
Make sure the generated SQL looks correct.
|
19
|
+
"""
|
20
|
+
querystring = GetChildElement(
|
21
|
+
GetChildElement(RecordingStudio.facilities, "a"), "b"
|
22
|
+
)
|
23
|
+
|
24
|
+
sql, query_args = querystring.compile_string()
|
25
|
+
|
26
|
+
self.assertEqual(
|
27
|
+
sql,
|
28
|
+
'"recording_studio"."facilities" -> $1 -> $2',
|
29
|
+
)
|
30
|
+
|
31
|
+
self.assertListEqual(query_args, ["a", "b"])
|
32
|
+
|
33
|
+
|
34
|
+
@engines_skip("sqlite")
|
35
|
+
class TestGetElementFromPath(TestCase):
|
36
|
+
|
37
|
+
def test_query(self):
|
38
|
+
"""
|
39
|
+
Make sure the generated SQL looks correct.
|
40
|
+
"""
|
41
|
+
querystring = GetElementFromPath(
|
42
|
+
RecordingStudio.facilities, ["a", "b"]
|
43
|
+
)
|
44
|
+
|
45
|
+
sql, query_args = querystring.compile_string()
|
46
|
+
|
47
|
+
self.assertEqual(
|
48
|
+
sql,
|
49
|
+
'"recording_studio"."facilities" #> $1',
|
50
|
+
)
|
51
|
+
|
52
|
+
self.assertListEqual(query_args, [["a", "b"]])
|
tests/table/test_insert.py
CHANGED
@@ -201,7 +201,7 @@ class TestOnConflict(TestCase):
|
|
201
201
|
Make sure that a composite unique constraint can be used as a target.
|
202
202
|
|
203
203
|
We only run it on Postgres and Cockroach because we use ALTER TABLE
|
204
|
-
to add a
|
204
|
+
to add a constraint, which SQLite doesn't support.
|
205
205
|
"""
|
206
206
|
Band = self.Band
|
207
207
|
|
File without changes
|
File without changes
|
File without changes
|