piccolo 1.6.0__py3-none-any.whl → 1.7.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/columns/column_types.py +26 -5
- piccolo/engine/sqlite.py +171 -57
- {piccolo-1.6.0.dist-info → piccolo-1.7.0.dist-info}/METADATA +1 -1
- {piccolo-1.6.0.dist-info → piccolo-1.7.0.dist-info}/RECORD +10 -10
- tests/columns/test_array.py +89 -2
- {piccolo-1.6.0.dist-info → piccolo-1.7.0.dist-info}/LICENSE +0 -0
- {piccolo-1.6.0.dist-info → piccolo-1.7.0.dist-info}/WHEEL +0 -0
- {piccolo-1.6.0.dist-info → piccolo-1.7.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.6.0.dist-info → piccolo-1.7.0.dist-info}/top_level.txt +0 -0
piccolo/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__VERSION__ = "1.
|
1
|
+
__VERSION__ = "1.7.0"
|
piccolo/columns/column_types.py
CHANGED
@@ -2532,7 +2532,14 @@ class Array(Column):
|
|
2532
2532
|
if engine_type in ("postgres", "cockroach"):
|
2533
2533
|
return f"{self.base_column.column_type}[]"
|
2534
2534
|
elif engine_type == "sqlite":
|
2535
|
-
|
2535
|
+
inner_column = self._get_inner_column()
|
2536
|
+
return (
|
2537
|
+
f"ARRAY_{inner_column.column_type}"
|
2538
|
+
if isinstance(
|
2539
|
+
inner_column, (Date, Timestamp, Timestamptz, Time)
|
2540
|
+
)
|
2541
|
+
else "ARRAY"
|
2542
|
+
)
|
2536
2543
|
raise Exception("Unrecognized engine type")
|
2537
2544
|
|
2538
2545
|
def _setup_base_column(self, table_class: t.Type[Table]):
|
@@ -2564,6 +2571,23 @@ class Array(Column):
|
|
2564
2571
|
else:
|
2565
2572
|
return start + 1
|
2566
2573
|
|
2574
|
+
def _get_inner_column(self) -> Column:
|
2575
|
+
"""
|
2576
|
+
A helper function to get the innermost ``Column`` for the array. For
|
2577
|
+
example::
|
2578
|
+
|
2579
|
+
>>> Array(Varchar())._get_inner_column()
|
2580
|
+
Varchar
|
2581
|
+
|
2582
|
+
>>> Array(Array(Varchar()))._get_inner_column()
|
2583
|
+
Varchar
|
2584
|
+
|
2585
|
+
"""
|
2586
|
+
if isinstance(self.base_column, Array):
|
2587
|
+
return self.base_column._get_inner_column()
|
2588
|
+
else:
|
2589
|
+
return self.base_column
|
2590
|
+
|
2567
2591
|
def _get_inner_value_type(self) -> t.Type:
|
2568
2592
|
"""
|
2569
2593
|
A helper function to get the innermost value type for the array. For
|
@@ -2576,10 +2600,7 @@ class Array(Column):
|
|
2576
2600
|
str
|
2577
2601
|
|
2578
2602
|
"""
|
2579
|
-
|
2580
|
-
return self.base_column._get_inner_value_type()
|
2581
|
-
else:
|
2582
|
-
return self.base_column.value_type
|
2603
|
+
return self._get_inner_column().value_type
|
2583
2604
|
|
2584
2605
|
def __getitem__(self, value: int) -> Array:
|
2585
2606
|
"""
|
piccolo/engine/sqlite.py
CHANGED
@@ -9,6 +9,7 @@ import typing as t
|
|
9
9
|
import uuid
|
10
10
|
from dataclasses import dataclass
|
11
11
|
from decimal import Decimal
|
12
|
+
from functools import partial, wraps
|
12
13
|
|
13
14
|
from piccolo.engine.base import Batch, Engine, validate_savepoint_name
|
14
15
|
from piccolo.engine.exceptions import TransactionError
|
@@ -35,14 +36,14 @@ if t.TYPE_CHECKING: # pragma: no cover
|
|
35
36
|
# In
|
36
37
|
|
37
38
|
|
38
|
-
def convert_numeric_in(value):
|
39
|
+
def convert_numeric_in(value: Decimal) -> float:
|
39
40
|
"""
|
40
41
|
Convert any Decimal values into floats.
|
41
42
|
"""
|
42
43
|
return float(value)
|
43
44
|
|
44
45
|
|
45
|
-
def convert_uuid_in(value) -> str:
|
46
|
+
def convert_uuid_in(value: uuid.UUID) -> str:
|
46
47
|
"""
|
47
48
|
Converts the UUID value being passed into sqlite.
|
48
49
|
"""
|
@@ -56,7 +57,7 @@ def convert_time_in(value: datetime.time) -> str:
|
|
56
57
|
return value.isoformat()
|
57
58
|
|
58
59
|
|
59
|
-
def convert_date_in(value: datetime.date):
|
60
|
+
def convert_date_in(value: datetime.date) -> str:
|
60
61
|
"""
|
61
62
|
Converts the date value being passed into sqlite.
|
62
63
|
"""
|
@@ -74,122 +75,235 @@ def convert_datetime_in(value: datetime.datetime) -> str:
|
|
74
75
|
return str(value)
|
75
76
|
|
76
77
|
|
77
|
-
def convert_timedelta_in(value: datetime.timedelta):
|
78
|
+
def convert_timedelta_in(value: datetime.timedelta) -> float:
|
78
79
|
"""
|
79
80
|
Converts the timedelta value being passed into sqlite.
|
80
81
|
"""
|
81
82
|
return value.total_seconds()
|
82
83
|
|
83
84
|
|
84
|
-
def convert_array_in(value: list):
|
85
|
+
def convert_array_in(value: list) -> str:
|
85
86
|
"""
|
86
|
-
Converts a list value into a string
|
87
|
+
Converts a list value into a string (it handles nested lists, and type like
|
88
|
+
dateime/ time / date which aren't usually JSON serialisable.).
|
89
|
+
|
87
90
|
"""
|
88
|
-
if value and type(value[0]) not in [str, int, float, list]:
|
89
|
-
raise ValueError("Can only serialise str, int, float, and list.")
|
90
91
|
|
91
|
-
|
92
|
+
def serialise(data: list):
|
93
|
+
output = []
|
94
|
+
|
95
|
+
for item in data:
|
96
|
+
if isinstance(item, list):
|
97
|
+
output.append(serialise(item))
|
98
|
+
elif isinstance(
|
99
|
+
item, (datetime.datetime, datetime.time, datetime.date)
|
100
|
+
):
|
101
|
+
if adapter := ADAPTERS.get(type(item)):
|
102
|
+
output.append(adapter(item))
|
103
|
+
else:
|
104
|
+
raise ValueError("The adapter wasn't found.")
|
105
|
+
elif item is None or isinstance(item, (str, int, float, list)):
|
106
|
+
# We can safely JSON serialise these.
|
107
|
+
output.append(item)
|
108
|
+
else:
|
109
|
+
raise ValueError("We can't currently serialise this value.")
|
110
|
+
|
111
|
+
return output
|
112
|
+
|
113
|
+
return dump_json(serialise(value))
|
114
|
+
|
115
|
+
|
116
|
+
###############################################################################
|
117
|
+
|
118
|
+
# Register adapters
|
119
|
+
|
120
|
+
ADAPTERS: t.Dict[t.Type, t.Callable[[t.Any], t.Any]] = {
|
121
|
+
Decimal: convert_numeric_in,
|
122
|
+
uuid.UUID: convert_uuid_in,
|
123
|
+
datetime.time: convert_time_in,
|
124
|
+
datetime.date: convert_date_in,
|
125
|
+
datetime.datetime: convert_datetime_in,
|
126
|
+
datetime.timedelta: convert_timedelta_in,
|
127
|
+
list: convert_array_in,
|
128
|
+
}
|
92
129
|
|
130
|
+
for value_type, adapter in ADAPTERS.items():
|
131
|
+
sqlite3.register_adapter(value_type, adapter)
|
132
|
+
|
133
|
+
###############################################################################
|
93
134
|
|
94
135
|
# Out
|
95
136
|
|
96
137
|
|
97
|
-
def
|
138
|
+
def decode_to_string(converter: t.Callable[[str], t.Any]):
|
139
|
+
"""
|
140
|
+
This means we can use our converters with string and bytes. They are
|
141
|
+
passed bytes when used directly via SQLite, and are passed strings when
|
142
|
+
used by the array converters.
|
143
|
+
"""
|
144
|
+
|
145
|
+
@wraps(converter)
|
146
|
+
def wrapper(value: t.Union[str, bytes]) -> t.Any:
|
147
|
+
if isinstance(value, bytes):
|
148
|
+
return converter(value.decode("utf8"))
|
149
|
+
elif isinstance(value, str):
|
150
|
+
return converter(value)
|
151
|
+
else:
|
152
|
+
raise ValueError("Unsupported type")
|
153
|
+
|
154
|
+
return wrapper
|
155
|
+
|
156
|
+
|
157
|
+
@decode_to_string
|
158
|
+
def convert_numeric_out(value: str) -> Decimal:
|
98
159
|
"""
|
99
160
|
Convert float values into Decimals.
|
100
161
|
"""
|
101
|
-
return Decimal(value
|
162
|
+
return Decimal(value)
|
102
163
|
|
103
164
|
|
104
|
-
|
165
|
+
@decode_to_string
|
166
|
+
def convert_int_out(value: str) -> int:
|
105
167
|
"""
|
106
168
|
Make sure Integer values are actually of type int.
|
107
169
|
"""
|
108
170
|
return int(float(value))
|
109
171
|
|
110
172
|
|
111
|
-
|
173
|
+
@decode_to_string
|
174
|
+
def convert_uuid_out(value: str) -> uuid.UUID:
|
112
175
|
"""
|
113
176
|
If the value is a uuid, convert it to a UUID instance.
|
114
177
|
"""
|
115
|
-
return uuid.UUID(value
|
178
|
+
return uuid.UUID(value)
|
116
179
|
|
117
180
|
|
118
|
-
|
119
|
-
|
181
|
+
@decode_to_string
|
182
|
+
def convert_date_out(value: str) -> datetime.date:
|
183
|
+
return datetime.date.fromisoformat(value)
|
120
184
|
|
121
185
|
|
122
|
-
|
186
|
+
@decode_to_string
|
187
|
+
def convert_time_out(value: str) -> datetime.time:
|
123
188
|
"""
|
124
189
|
If the value is a time, convert it to a UUID instance.
|
125
190
|
"""
|
126
|
-
return datetime.time.fromisoformat(value
|
191
|
+
return datetime.time.fromisoformat(value)
|
127
192
|
|
128
193
|
|
129
|
-
|
194
|
+
@decode_to_string
|
195
|
+
def convert_seconds_out(value: str) -> datetime.timedelta:
|
130
196
|
"""
|
131
197
|
If the value is from a seconds column, convert it to a timedelta instance.
|
132
198
|
"""
|
133
|
-
return datetime.timedelta(seconds=float(value
|
199
|
+
return datetime.timedelta(seconds=float(value))
|
134
200
|
|
135
201
|
|
136
|
-
|
202
|
+
@decode_to_string
|
203
|
+
def convert_boolean_out(value: str) -> bool:
|
137
204
|
"""
|
138
205
|
If the value is from a boolean column, convert it to a bool value.
|
139
206
|
"""
|
140
|
-
|
141
|
-
return _value == "1"
|
207
|
+
return value == "1"
|
142
208
|
|
143
209
|
|
144
|
-
|
210
|
+
@decode_to_string
|
211
|
+
def convert_timestamp_out(value: str) -> datetime.datetime:
|
145
212
|
"""
|
146
213
|
If the value is from a timestamp column, convert it to a datetime value.
|
147
214
|
"""
|
148
|
-
return datetime.datetime.fromisoformat(value
|
215
|
+
return datetime.datetime.fromisoformat(value)
|
149
216
|
|
150
217
|
|
151
|
-
|
218
|
+
@decode_to_string
|
219
|
+
def convert_timestamptz_out(value: str) -> datetime.datetime:
|
152
220
|
"""
|
153
221
|
If the value is from a timestamptz column, convert it to a datetime value,
|
154
222
|
with a timezone of UTC.
|
155
223
|
"""
|
156
|
-
|
157
|
-
|
158
|
-
|
224
|
+
return datetime.datetime.fromisoformat(value).replace(
|
225
|
+
tzinfo=datetime.timezone.utc
|
226
|
+
)
|
159
227
|
|
160
228
|
|
161
|
-
|
229
|
+
@decode_to_string
|
230
|
+
def convert_array_out(value: str) -> t.List:
|
162
231
|
"""
|
163
232
|
If the value if from an array column, deserialise the string back into a
|
164
233
|
list.
|
165
234
|
"""
|
166
|
-
return load_json(value
|
167
|
-
|
168
|
-
|
169
|
-
def
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
235
|
+
return load_json(value)
|
236
|
+
|
237
|
+
|
238
|
+
def convert_complex_array_out(value: bytes, converter: t.Callable):
|
239
|
+
"""
|
240
|
+
This is used to handle arrays of things like timestamps, which we can't
|
241
|
+
just load from JSON without doing additional work to convert the elements
|
242
|
+
back into Python objects.
|
243
|
+
"""
|
244
|
+
parsed = load_json(value.decode("utf8"))
|
245
|
+
|
246
|
+
def convert_list(list_value: t.List):
|
247
|
+
output = []
|
248
|
+
|
249
|
+
for value in list_value:
|
250
|
+
if isinstance(value, list):
|
251
|
+
# For nested arrays
|
252
|
+
output.append(convert_list(value))
|
253
|
+
elif isinstance(value, str):
|
254
|
+
output.append(converter(value))
|
255
|
+
else:
|
256
|
+
output.append(value)
|
257
|
+
|
258
|
+
return output
|
259
|
+
|
260
|
+
if isinstance(parsed, list):
|
261
|
+
return convert_list(parsed)
|
262
|
+
else:
|
263
|
+
return parsed
|
264
|
+
|
265
|
+
|
266
|
+
@decode_to_string
|
267
|
+
def convert_M2M_out(value: str) -> t.List:
|
268
|
+
return value.split(",")
|
269
|
+
|
270
|
+
|
271
|
+
###############################################################################
|
272
|
+
# Register the basic converters
|
273
|
+
|
274
|
+
CONVERTERS = {
|
275
|
+
"NUMERIC": convert_numeric_out,
|
276
|
+
"INTEGER": convert_int_out,
|
277
|
+
"UUID": convert_uuid_out,
|
278
|
+
"DATE": convert_date_out,
|
279
|
+
"TIME": convert_time_out,
|
280
|
+
"SECONDS": convert_seconds_out,
|
281
|
+
"BOOLEAN": convert_boolean_out,
|
282
|
+
"TIMESTAMP": convert_timestamp_out,
|
283
|
+
"TIMESTAMPTZ": convert_timestamptz_out,
|
284
|
+
"M2M": convert_M2M_out,
|
285
|
+
}
|
286
|
+
|
287
|
+
for column_name, converter in CONVERTERS.items():
|
288
|
+
sqlite3.register_converter(column_name, converter)
|
289
|
+
|
290
|
+
###############################################################################
|
291
|
+
# Register the array converters
|
292
|
+
|
293
|
+
# The ARRAY column type handles values which can be easily serialised to and
|
294
|
+
# from JSON.
|
295
|
+
sqlite3.register_converter("ARRAY", convert_array_out)
|
296
|
+
|
297
|
+
# We have special column types for arrays of timestamps etc, as simply loading
|
298
|
+
# the JSON isn't sufficient.
|
299
|
+
for column_name in ("TIMESTAMP", "TIMESTAMPTZ", "DATE", "TIME"):
|
300
|
+
sqlite3.register_converter(
|
301
|
+
f"ARRAY_{column_name}",
|
302
|
+
partial(
|
303
|
+
convert_complex_array_out,
|
304
|
+
converter=CONVERTERS[column_name],
|
305
|
+
),
|
306
|
+
)
|
193
307
|
|
194
308
|
###############################################################################
|
195
309
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
piccolo/__init__.py,sha256=
|
1
|
+
piccolo/__init__.py,sha256=h4xA2N06XGCcrqnSQbI2FfqW4dGycKVDmFXCAklesxA,22
|
2
2
|
piccolo/custom_types.py,sha256=7HMQAze-5mieNLfbQ5QgbRQgR2abR7ol0qehv2SqROY,604
|
3
3
|
piccolo/main.py,sha256=1VsFV67FWTUikPTysp64Fmgd9QBVa_9wcwKfwj2UCEA,5117
|
4
4
|
piccolo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -117,7 +117,7 @@ piccolo/apps/user/piccolo_migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
|
|
117
117
|
piccolo/columns/__init__.py,sha256=OYhO_n9anMiU9nL-K6ATq9FhAtm8RyMpqYQ7fTVbhxI,1120
|
118
118
|
piccolo/columns/base.py,sha256=XUhhx-wNc6nBPd39VIYuNfFERTaFzow9SHGfZjJ2YC0,31288
|
119
119
|
piccolo/columns/choices.py,sha256=-HNQuk9vMmVZIPZ5PMeXGTfr23o4nzKPSAkvcG1k0y8,723
|
120
|
-
piccolo/columns/column_types.py,sha256=
|
120
|
+
piccolo/columns/column_types.py,sha256=CzbNnP_VWvz6_r4aaRcMHiHZOaWHeq5IGaN8WJ7JGPA,81685
|
121
121
|
piccolo/columns/combination.py,sha256=vMXC2dfY7pvnCFhsT71XFVyb4gdQzfRsCMaiduu04Ss,6900
|
122
122
|
piccolo/columns/indexes.py,sha256=NfNok3v_791jgDlN28KmhP9ZCjl6031BXmjxV3ovXJk,372
|
123
123
|
piccolo/columns/m2m.py,sha256=vRJZqBcBP3TQ9Mmb7UEqTgg0QoxIIjIu6JfGLAi4X8Q,14595
|
@@ -144,7 +144,7 @@ piccolo/engine/cockroach.py,sha256=7anXR3JPpGuR6-OpDLHM0FxKZhjuTvbRUuZV6fv9lXU,1
|
|
144
144
|
piccolo/engine/exceptions.py,sha256=X8xZiTF-L9PIqFT-KDXnv1jFIIOZMF8fYK692chttJE,44
|
145
145
|
piccolo/engine/finder.py,sha256=GjzBNtzRzH79fjtRn7OI3nZiOXE8JfoQWAvHVPrPNx4,507
|
146
146
|
piccolo/engine/postgres.py,sha256=zUY6x52QrZ8waiqEUuqlVFiXyzAXrsFi3PY5EJnv3DM,18276
|
147
|
-
piccolo/engine/sqlite.py,sha256=
|
147
|
+
piccolo/engine/sqlite.py,sha256=edwACs4RWsbzgoozBJRN5l9_Vq4nCMefRRLzuZYsF7M,25033
|
148
148
|
piccolo/query/__init__.py,sha256=bcsMV4813rMRAIqGv4DxI4eyO4FmpXkDv9dfTk5pt3A,699
|
149
149
|
piccolo/query/base.py,sha256=G8Mwz0GcHY4Xs5Co9ubCNMI-3orfOsDdRDOnFRws7TU,15212
|
150
150
|
piccolo/query/mixins.py,sha256=1RyhORDRwTZF9m_2uEgc6sOSd2uViXivBAaFN8geq5g,21982
|
@@ -241,7 +241,7 @@ tests/apps/user/commands/test_change_permissions.py,sha256=uVKEiT1EKot3VA2TDETdQ
|
|
241
241
|
tests/apps/user/commands/test_create.py,sha256=iJ3Tti62rHwvdcTwNXrc5JPam6vR1qxKRdMN456vm3o,2250
|
242
242
|
tests/apps/user/commands/test_list.py,sha256=ipPfGdW6fH7q-Jc7JcYUvlioGmH9GQU0WImZGC2m-XQ,2840
|
243
243
|
tests/columns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
244
|
-
tests/columns/test_array.py,sha256=
|
244
|
+
tests/columns/test_array.py,sha256=Kd8yy3C4cXJIgutY-cv0MrE4mvgr52qqVUdkcazW_uI,9298
|
245
245
|
tests/columns/test_base.py,sha256=CTqCNcrqAJTjLXe3MCZgTczrmB3jcVRcOpU4FilpLoQ,3918
|
246
246
|
tests/columns/test_bigint.py,sha256=a0B4y1H02ww5qaW574X2lyenbY6o29ztOhiaqybPC0c,1149
|
247
247
|
tests/columns/test_boolean.py,sha256=kDESp6FnRtSZhuqIu0dBRwKMSpS5TFbbs3sz2MyZSs8,1720
|
@@ -355,9 +355,9 @@ tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5
|
|
355
355
|
tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
|
356
356
|
tests/utils/test_table_reflection.py,sha256=SIzuat-IpcVj1GCFyOWKShI8YkhdOPPFH7qVrvfyPNE,3794
|
357
357
|
tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
|
358
|
-
piccolo-1.
|
359
|
-
piccolo-1.
|
360
|
-
piccolo-1.
|
361
|
-
piccolo-1.
|
362
|
-
piccolo-1.
|
363
|
-
piccolo-1.
|
358
|
+
piccolo-1.7.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
|
359
|
+
piccolo-1.7.0.dist-info/METADATA,sha256=AIBs_jqCxL694qW6TwjwE0C4_LF_RqzaVCZ_-6Ag6po,5177
|
360
|
+
piccolo-1.7.0.dist-info/WHEEL,sha256=00yskusixUoUt5ob_CiUp6LsnN5lqzTJpoqOFg_FVIc,92
|
361
|
+
piccolo-1.7.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
|
362
|
+
piccolo-1.7.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
|
363
|
+
piccolo-1.7.0.dist-info/RECORD,,
|
tests/columns/test_array.py
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
+
import datetime
|
1
2
|
from unittest import TestCase
|
2
3
|
|
3
|
-
from piccolo.columns.column_types import
|
4
|
+
from piccolo.columns.column_types import (
|
5
|
+
Array,
|
6
|
+
BigInt,
|
7
|
+
Date,
|
8
|
+
Integer,
|
9
|
+
Time,
|
10
|
+
Timestamp,
|
11
|
+
Timestamptz,
|
12
|
+
)
|
4
13
|
from piccolo.table import Table
|
5
14
|
from tests.base import engines_only, sqlite_only
|
6
15
|
|
@@ -22,7 +31,7 @@ class TestArrayDefault(TestCase):
|
|
22
31
|
|
23
32
|
class TestArray(TestCase):
|
24
33
|
"""
|
25
|
-
Make sure an Array column can be created, and
|
34
|
+
Make sure an Array column can be created, and works correctly.
|
26
35
|
"""
|
27
36
|
|
28
37
|
def setUp(self):
|
@@ -166,6 +175,84 @@ class TestArray(TestCase):
|
|
166
175
|
)
|
167
176
|
|
168
177
|
|
178
|
+
###############################################################################
|
179
|
+
# Date and time arrays
|
180
|
+
|
181
|
+
|
182
|
+
class DateTimeArrayTable(Table):
|
183
|
+
date = Array(Date())
|
184
|
+
time = Array(Time())
|
185
|
+
timestamp = Array(Timestamp())
|
186
|
+
timestamptz = Array(Timestamptz())
|
187
|
+
date_nullable = Array(Date(), null=True)
|
188
|
+
time_nullable = Array(Time(), null=True)
|
189
|
+
timestamp_nullable = Array(Timestamp(), null=True)
|
190
|
+
timestamptz_nullable = Array(Timestamptz(), null=True)
|
191
|
+
|
192
|
+
|
193
|
+
class TestDateTimeArray(TestCase):
|
194
|
+
"""
|
195
|
+
Make sure that data can be stored and retrieved when using arrays of
|
196
|
+
date / time / timestamp.
|
197
|
+
|
198
|
+
We have to serialise / deserialise it in a special way in SQLite, hence
|
199
|
+
the tests.
|
200
|
+
|
201
|
+
"""
|
202
|
+
|
203
|
+
def setUp(self):
|
204
|
+
DateTimeArrayTable.create_table().run_sync()
|
205
|
+
|
206
|
+
def tearDown(self):
|
207
|
+
DateTimeArrayTable.alter().drop_table().run_sync()
|
208
|
+
|
209
|
+
@engines_only("postgres", "sqlite")
|
210
|
+
def test_storage(self):
|
211
|
+
test_date = datetime.date(year=2024, month=1, day=1)
|
212
|
+
test_time = datetime.time(hour=12, minute=0)
|
213
|
+
test_timestamp = datetime.datetime(
|
214
|
+
year=2024, month=1, day=1, hour=12, minute=0
|
215
|
+
)
|
216
|
+
test_timestamptz = datetime.datetime(
|
217
|
+
year=2024,
|
218
|
+
month=1,
|
219
|
+
day=1,
|
220
|
+
hour=12,
|
221
|
+
minute=0,
|
222
|
+
tzinfo=datetime.timezone.utc,
|
223
|
+
)
|
224
|
+
|
225
|
+
DateTimeArrayTable(
|
226
|
+
{
|
227
|
+
DateTimeArrayTable.date: [test_date],
|
228
|
+
DateTimeArrayTable.time: [test_time],
|
229
|
+
DateTimeArrayTable.timestamp: [test_timestamp],
|
230
|
+
DateTimeArrayTable.timestamptz: [test_timestamptz],
|
231
|
+
DateTimeArrayTable.date_nullable: None,
|
232
|
+
DateTimeArrayTable.time_nullable: None,
|
233
|
+
DateTimeArrayTable.timestamp_nullable: None,
|
234
|
+
DateTimeArrayTable.timestamptz_nullable: None,
|
235
|
+
}
|
236
|
+
).save().run_sync()
|
237
|
+
|
238
|
+
row = DateTimeArrayTable.objects().first().run_sync()
|
239
|
+
assert row is not None
|
240
|
+
|
241
|
+
self.assertListEqual(row.date, [test_date])
|
242
|
+
self.assertListEqual(row.time, [test_time])
|
243
|
+
self.assertListEqual(row.timestamp, [test_timestamp])
|
244
|
+
self.assertListEqual(row.timestamptz, [test_timestamptz])
|
245
|
+
|
246
|
+
self.assertIsNone(row.date_nullable)
|
247
|
+
self.assertIsNone(row.time_nullable)
|
248
|
+
self.assertIsNone(row.timestamp_nullable)
|
249
|
+
self.assertIsNone(row.timestamptz_nullable)
|
250
|
+
|
251
|
+
|
252
|
+
###############################################################################
|
253
|
+
# Nested arrays
|
254
|
+
|
255
|
+
|
169
256
|
class NestedArrayTable(Table):
|
170
257
|
value = Array(base_column=Array(base_column=BigInt()))
|
171
258
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|