piccolo 1.27.1__py3-none-any.whl → 1.28.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/app/commands/new.py +3 -3
- piccolo/apps/asgi/commands/new.py +1 -2
- piccolo/apps/fixtures/commands/dump.py +8 -8
- piccolo/apps/fixtures/commands/load.py +5 -5
- piccolo/apps/fixtures/commands/shared.py +9 -9
- piccolo/apps/migrations/auto/diffable_table.py +12 -12
- piccolo/apps/migrations/auto/migration_manager.py +59 -66
- piccolo/apps/migrations/auto/operations.py +14 -14
- piccolo/apps/migrations/auto/schema_differ.py +35 -34
- piccolo/apps/migrations/auto/schema_snapshot.py +3 -4
- piccolo/apps/migrations/auto/serialisation.py +27 -24
- piccolo/apps/migrations/auto/serialisation_legacy.py +2 -2
- piccolo/apps/migrations/commands/backwards.py +1 -2
- piccolo/apps/migrations/commands/base.py +12 -12
- piccolo/apps/migrations/commands/check.py +2 -3
- piccolo/apps/migrations/commands/clean.py +3 -3
- piccolo/apps/migrations/commands/forwards.py +1 -2
- piccolo/apps/migrations/commands/new.py +6 -6
- piccolo/apps/migrations/tables.py +3 -3
- piccolo/apps/playground/commands/run.py +29 -13
- piccolo/apps/schema/commands/generate.py +49 -49
- piccolo/apps/schema/commands/graph.py +5 -5
- piccolo/apps/shell/commands/run.py +1 -2
- piccolo/apps/sql_shell/commands/run.py +4 -4
- piccolo/apps/tester/commands/run.py +3 -3
- piccolo/apps/user/commands/change_permissions.py +6 -6
- piccolo/apps/user/commands/create.py +7 -7
- piccolo/apps/user/commands/list.py +2 -2
- piccolo/apps/user/tables.py +8 -8
- piccolo/columns/base.py +84 -52
- piccolo/columns/choices.py +2 -2
- piccolo/columns/column_types.py +297 -175
- piccolo/columns/combination.py +15 -12
- piccolo/columns/defaults/base.py +4 -4
- piccolo/columns/defaults/date.py +4 -3
- piccolo/columns/defaults/interval.py +4 -3
- piccolo/columns/defaults/time.py +4 -3
- piccolo/columns/defaults/timestamp.py +4 -3
- piccolo/columns/defaults/timestamptz.py +4 -3
- piccolo/columns/defaults/uuid.py +3 -2
- piccolo/columns/m2m.py +28 -35
- piccolo/columns/readable.py +4 -3
- piccolo/columns/reference.py +9 -9
- piccolo/conf/apps.py +53 -54
- piccolo/custom_types.py +28 -6
- piccolo/engine/base.py +14 -14
- piccolo/engine/cockroach.py +5 -4
- piccolo/engine/finder.py +2 -2
- piccolo/engine/postgres.py +20 -19
- piccolo/engine/sqlite.py +23 -22
- piccolo/query/base.py +30 -29
- piccolo/query/functions/__init__.py +12 -0
- piccolo/query/functions/aggregate.py +4 -3
- piccolo/query/functions/array.py +151 -0
- piccolo/query/functions/base.py +3 -3
- piccolo/query/functions/datetime.py +22 -22
- piccolo/query/functions/string.py +4 -4
- piccolo/query/functions/type_conversion.py +30 -15
- piccolo/query/methods/alter.py +47 -46
- piccolo/query/methods/count.py +11 -10
- piccolo/query/methods/create.py +6 -5
- piccolo/query/methods/create_index.py +9 -8
- piccolo/query/methods/delete.py +7 -6
- piccolo/query/methods/drop_index.py +7 -6
- piccolo/query/methods/exists.py +6 -5
- piccolo/query/methods/indexes.py +4 -4
- piccolo/query/methods/insert.py +21 -14
- piccolo/query/methods/objects.py +60 -50
- piccolo/query/methods/raw.py +7 -6
- piccolo/query/methods/refresh.py +8 -7
- piccolo/query/methods/select.py +56 -49
- piccolo/query/methods/table_exists.py +5 -5
- piccolo/query/methods/update.py +8 -7
- piccolo/query/mixins.py +56 -61
- piccolo/query/operators/json.py +11 -11
- piccolo/query/proxy.py +8 -9
- piccolo/querystring.py +14 -15
- piccolo/schema.py +10 -10
- piccolo/table.py +93 -94
- piccolo/table_reflection.py +9 -9
- piccolo/testing/model_builder.py +12 -11
- piccolo/testing/random_builder.py +2 -2
- piccolo/testing/test_case.py +4 -4
- piccolo/utils/dictionary.py +3 -3
- piccolo/utils/encoding.py +5 -5
- piccolo/utils/lazy_loader.py +3 -3
- piccolo/utils/list.py +7 -8
- piccolo/utils/objects.py +4 -6
- piccolo/utils/pydantic.py +21 -24
- piccolo/utils/sql_values.py +3 -3
- piccolo/utils/sync.py +4 -3
- piccolo/utils/warnings.py +1 -2
- {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/METADATA +1 -1
- {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/RECORD +122 -121
- tests/apps/fixtures/commands/test_dump_load.py +1 -2
- tests/apps/migrations/auto/integration/test_migrations.py +32 -7
- tests/apps/migrations/auto/test_migration_manager.py +2 -2
- tests/apps/migrations/auto/test_schema_differ.py +22 -23
- tests/apps/migrations/commands/test_forwards_backwards.py +3 -3
- tests/columns/m2m/base.py +2 -2
- tests/columns/test_array.py +176 -10
- tests/columns/test_boolean.py +2 -4
- tests/columns/test_combination.py +29 -1
- tests/columns/test_db_column_name.py +2 -2
- tests/engine/test_extra_nodes.py +2 -2
- tests/engine/test_pool.py +3 -3
- tests/engine/test_transaction.py +4 -4
- tests/query/test_freeze.py +4 -4
- tests/table/instance/test_get_related.py +2 -2
- tests/table/test_alter.py +4 -4
- tests/table/test_indexes.py +1 -2
- tests/table/test_refresh.py +2 -2
- tests/table/test_select.py +58 -0
- tests/table/test_update.py +3 -3
- tests/testing/test_model_builder.py +1 -2
- tests/utils/test_pydantic.py +36 -36
- tests/utils/test_table_reflection.py +1 -2
- {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/WHEEL +0 -0
- {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/licenses/LICENSE +0 -0
- {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/top_level.txt +0 -0
piccolo/query/mixins.py
CHANGED
@@ -3,9 +3,10 @@ from __future__ import annotations
|
|
3
3
|
import asyncio
|
4
4
|
import collections.abc
|
5
5
|
import itertools
|
6
|
-
import
|
6
|
+
from collections.abc import Callable, Sequence
|
7
7
|
from dataclasses import dataclass, field
|
8
8
|
from enum import Enum, auto
|
9
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
9
10
|
|
10
11
|
from piccolo.columns import And, Column, Or, Where
|
11
12
|
from piccolo.columns.column_types import ForeignKey
|
@@ -15,7 +16,7 @@ from piccolo.querystring import QueryString
|
|
15
16
|
from piccolo.utils.list import flatten
|
16
17
|
from piccolo.utils.sql_values import convert_to_sql_value
|
17
18
|
|
18
|
-
if
|
19
|
+
if TYPE_CHECKING: # pragma: no cover
|
19
20
|
from piccolo.querystring import Selectable
|
20
21
|
from piccolo.table import Table # noqa
|
21
22
|
|
@@ -33,7 +34,7 @@ class Distinct:
|
|
33
34
|
__slots__ = ("enabled", "on")
|
34
35
|
|
35
36
|
enabled: bool
|
36
|
-
on:
|
37
|
+
on: Optional[Sequence[Column]]
|
37
38
|
|
38
39
|
@property
|
39
40
|
def querystring(self) -> QueryString:
|
@@ -152,17 +153,17 @@ class OrderByRaw:
|
|
152
153
|
class OrderByItem:
|
153
154
|
__slots__ = ("columns", "ascending")
|
154
155
|
|
155
|
-
columns:
|
156
|
+
columns: Sequence[Union[Column, OrderByRaw]]
|
156
157
|
ascending: bool
|
157
158
|
|
158
159
|
|
159
160
|
@dataclass
|
160
161
|
class OrderBy:
|
161
|
-
order_by_items:
|
162
|
+
order_by_items: list[OrderByItem] = field(default_factory=list)
|
162
163
|
|
163
164
|
@property
|
164
165
|
def querystring(self) -> QueryString:
|
165
|
-
order_by_strings:
|
166
|
+
order_by_strings: list[str] = []
|
166
167
|
for order_by_item in self.order_by_items:
|
167
168
|
order = "ASC" if order_by_item.ascending else "DESC"
|
168
169
|
for column in order_by_item.columns:
|
@@ -185,7 +186,7 @@ class OrderBy:
|
|
185
186
|
class Returning:
|
186
187
|
__slots__ = ("columns",)
|
187
188
|
|
188
|
-
columns:
|
189
|
+
columns: list[Column]
|
189
190
|
|
190
191
|
@property
|
191
192
|
def querystring(self) -> QueryString:
|
@@ -230,13 +231,13 @@ class CallbackType(Enum):
|
|
230
231
|
@dataclass
|
231
232
|
class Callback:
|
232
233
|
kind: CallbackType
|
233
|
-
target:
|
234
|
+
target: Callable
|
234
235
|
|
235
236
|
|
236
237
|
@dataclass
|
237
238
|
class WhereDelegate:
|
238
|
-
_where:
|
239
|
-
_where_columns:
|
239
|
+
_where: Optional[Combinable] = None
|
240
|
+
_where_columns: list[Column] = field(default_factory=list)
|
240
241
|
|
241
242
|
def get_where_columns(self):
|
242
243
|
"""
|
@@ -257,7 +258,7 @@ class WhereDelegate:
|
|
257
258
|
elif isinstance(combinable, WhereRaw):
|
258
259
|
self._where_columns.extend(combinable.querystring.columns)
|
259
260
|
|
260
|
-
def where(self, *where:
|
261
|
+
def where(self, *where: Union[Combinable, QueryString]):
|
261
262
|
for arg in where:
|
262
263
|
if isinstance(arg, bool):
|
263
264
|
raise ValueError(
|
@@ -278,7 +279,7 @@ class WhereDelegate:
|
|
278
279
|
class OrderByDelegate:
|
279
280
|
_order_by: OrderBy = field(default_factory=OrderBy)
|
280
281
|
|
281
|
-
def get_order_by_columns(self) ->
|
282
|
+
def get_order_by_columns(self) -> list[Column]:
|
282
283
|
"""
|
283
284
|
Used to work out which columns are needed for joins.
|
284
285
|
"""
|
@@ -290,7 +291,7 @@ class OrderByDelegate:
|
|
290
291
|
if isinstance(i, Column)
|
291
292
|
]
|
292
293
|
|
293
|
-
def order_by(self, *columns:
|
294
|
+
def order_by(self, *columns: Union[Column, OrderByRaw], ascending=True):
|
294
295
|
if len(columns) < 1:
|
295
296
|
raise ValueError("At least one column must be passed to order_by.")
|
296
297
|
|
@@ -301,7 +302,7 @@ class OrderByDelegate:
|
|
301
302
|
|
302
303
|
@dataclass
|
303
304
|
class LimitDelegate:
|
304
|
-
_limit:
|
305
|
+
_limit: Optional[Limit] = None
|
305
306
|
_first: bool = False
|
306
307
|
|
307
308
|
def limit(self, number: int):
|
@@ -319,7 +320,7 @@ class AsOfDelegate:
|
|
319
320
|
Currently supports Cockroach using AS OF SYSTEM TIME.
|
320
321
|
"""
|
321
322
|
|
322
|
-
_as_of:
|
323
|
+
_as_of: Optional[AsOf] = None
|
323
324
|
|
324
325
|
def as_of(self, interval: str = "-1s"):
|
325
326
|
self._as_of = AsOf(interval)
|
@@ -331,9 +332,7 @@ class DistinctDelegate:
|
|
331
332
|
default_factory=lambda: Distinct(enabled=False, on=None)
|
332
333
|
)
|
333
334
|
|
334
|
-
def distinct(
|
335
|
-
self, enabled: bool, on: t.Optional[t.Sequence[Column]] = None
|
336
|
-
):
|
335
|
+
def distinct(self, enabled: bool, on: Optional[Sequence[Column]] = None):
|
337
336
|
if on and not isinstance(on, collections.abc.Sequence):
|
338
337
|
# Check a sequence is passed in, otherwise the user will get some
|
339
338
|
# unuseful errors later on.
|
@@ -344,9 +343,9 @@ class DistinctDelegate:
|
|
344
343
|
|
345
344
|
@dataclass
|
346
345
|
class ReturningDelegate:
|
347
|
-
_returning:
|
346
|
+
_returning: Optional[Returning] = None
|
348
347
|
|
349
|
-
def returning(self, columns:
|
348
|
+
def returning(self, columns: Sequence[Column]):
|
350
349
|
self._returning = Returning(columns=list(columns))
|
351
350
|
|
352
351
|
|
@@ -360,9 +359,9 @@ class CountDelegate:
|
|
360
359
|
|
361
360
|
@dataclass
|
362
361
|
class AddDelegate:
|
363
|
-
_add:
|
362
|
+
_add: list[Table] = field(default_factory=list)
|
364
363
|
|
365
|
-
def add(self, *instances: Table, table_class:
|
364
|
+
def add(self, *instances: Table, table_class: type[Table]):
|
366
365
|
for instance in instances:
|
367
366
|
if not isinstance(instance, table_class):
|
368
367
|
raise TypeError("Incompatible type added.")
|
@@ -384,10 +383,10 @@ class OutputDelegate:
|
|
384
383
|
|
385
384
|
def output(
|
386
385
|
self,
|
387
|
-
as_list:
|
388
|
-
as_json:
|
389
|
-
load_json:
|
390
|
-
nested:
|
386
|
+
as_list: Optional[bool] = None,
|
387
|
+
as_json: Optional[bool] = None,
|
388
|
+
load_json: Optional[bool] = None,
|
389
|
+
nested: Optional[bool] = None,
|
391
390
|
):
|
392
391
|
"""
|
393
392
|
:param as_list:
|
@@ -429,13 +428,13 @@ class CallbackDelegate:
|
|
429
428
|
.callback([handler1, handler2])
|
430
429
|
"""
|
431
430
|
|
432
|
-
_callbacks:
|
431
|
+
_callbacks: dict[CallbackType, list[Callback]] = field(
|
433
432
|
default_factory=lambda: {kind: [] for kind in CallbackType}
|
434
433
|
)
|
435
434
|
|
436
435
|
def callback(
|
437
436
|
self,
|
438
|
-
callbacks:
|
437
|
+
callbacks: Union[Callable, list[Callable]],
|
439
438
|
*,
|
440
439
|
on: CallbackType,
|
441
440
|
):
|
@@ -446,7 +445,7 @@ class CallbackDelegate:
|
|
446
445
|
else:
|
447
446
|
self._callbacks[on].append(Callback(kind=on, target=callbacks))
|
448
447
|
|
449
|
-
async def invoke(self, results:
|
448
|
+
async def invoke(self, results: Any, *, kind: CallbackType) -> Any:
|
450
449
|
"""
|
451
450
|
Utility function that invokes the registered callbacks in the correct
|
452
451
|
way, handling both sync and async callbacks. Only callbacks of the
|
@@ -472,9 +471,9 @@ class PrefetchDelegate:
|
|
472
471
|
.prefetch(MyTable.column_a, MyTable.column_b)
|
473
472
|
"""
|
474
473
|
|
475
|
-
fk_columns:
|
474
|
+
fk_columns: list[ForeignKey] = field(default_factory=list)
|
476
475
|
|
477
|
-
def prefetch(self, *fk_columns:
|
476
|
+
def prefetch(self, *fk_columns: Union[ForeignKey, list[ForeignKey]]):
|
478
477
|
"""
|
479
478
|
:param columns:
|
480
479
|
We accept ``ForeignKey`` and ``List[ForeignKey]`` here, in case
|
@@ -482,7 +481,7 @@ class PrefetchDelegate:
|
|
482
481
|
in which case we flatten the list.
|
483
482
|
|
484
483
|
"""
|
485
|
-
_fk_columns:
|
484
|
+
_fk_columns: list[ForeignKey] = []
|
486
485
|
for column in fk_columns:
|
487
486
|
if isinstance(column, list):
|
488
487
|
_fk_columns.extend(column)
|
@@ -501,9 +500,9 @@ class ColumnsDelegate:
|
|
501
500
|
.columns(MyTable.column_a, MyTable.column_b)
|
502
501
|
"""
|
503
502
|
|
504
|
-
selected_columns:
|
503
|
+
selected_columns: Sequence[Selectable] = field(default_factory=list)
|
505
504
|
|
506
|
-
def columns(self, *columns:
|
505
|
+
def columns(self, *columns: Union[Selectable, list[Selectable]]):
|
507
506
|
"""
|
508
507
|
:param columns:
|
509
508
|
We accept ``Selectable`` and ``List[Selectable]`` here, in case
|
@@ -531,10 +530,10 @@ class ValuesDelegate:
|
|
531
530
|
Used to specify new column values - primarily used in update queries.
|
532
531
|
"""
|
533
532
|
|
534
|
-
table:
|
535
|
-
_values:
|
533
|
+
table: type[Table]
|
534
|
+
_values: dict[Column, Any] = field(default_factory=dict)
|
536
535
|
|
537
|
-
def values(self, values:
|
536
|
+
def values(self, values: dict[Union[Column, str], Any]):
|
538
537
|
"""
|
539
538
|
Example usage:
|
540
539
|
|
@@ -549,7 +548,7 @@ class ValuesDelegate:
|
|
549
548
|
.values(column_a=1})
|
550
549
|
|
551
550
|
"""
|
552
|
-
cleaned_values:
|
551
|
+
cleaned_values: dict[Column, Any] = {}
|
553
552
|
for key, value in values.items():
|
554
553
|
if isinstance(key, Column):
|
555
554
|
column = key
|
@@ -564,7 +563,7 @@ class ValuesDelegate:
|
|
564
563
|
|
565
564
|
self._values.update(cleaned_values)
|
566
565
|
|
567
|
-
def get_sql_values(self) ->
|
566
|
+
def get_sql_values(self) -> list[Any]:
|
568
567
|
"""
|
569
568
|
Convert any Enums into values, and serialise any JSON.
|
570
569
|
"""
|
@@ -587,7 +586,7 @@ class OffsetDelegate:
|
|
587
586
|
|
588
587
|
"""
|
589
588
|
|
590
|
-
_offset:
|
589
|
+
_offset: Optional[Offset] = None
|
591
590
|
|
592
591
|
def offset(self, number: int = 0):
|
593
592
|
self._offset = Offset(number)
|
@@ -597,7 +596,7 @@ class OffsetDelegate:
|
|
597
596
|
class GroupBy:
|
598
597
|
__slots__ = ("columns",)
|
599
598
|
|
600
|
-
columns:
|
599
|
+
columns: Sequence[Column]
|
601
600
|
|
602
601
|
@property
|
603
602
|
def querystring(self) -> QueryString:
|
@@ -620,7 +619,7 @@ class GroupByDelegate:
|
|
620
619
|
|
621
620
|
"""
|
622
621
|
|
623
|
-
_group_by:
|
622
|
+
_group_by: Optional[GroupBy] = None
|
624
623
|
|
625
624
|
def group_by(self, *columns: Column):
|
626
625
|
self._group_by = GroupBy(columns=columns)
|
@@ -637,12 +636,10 @@ class OnConflictAction(str, Enum):
|
|
637
636
|
|
638
637
|
@dataclass
|
639
638
|
class OnConflictItem:
|
640
|
-
target:
|
641
|
-
action:
|
642
|
-
values:
|
643
|
-
|
644
|
-
)
|
645
|
-
where: t.Optional[Combinable] = None
|
639
|
+
target: Optional[Union[str, Column, tuple[Column, ...]]] = None
|
640
|
+
action: Optional[OnConflictAction] = None
|
641
|
+
values: Optional[Sequence[Union[Column, tuple[Column, Any]]]] = None
|
642
|
+
where: Optional[Combinable] = None
|
646
643
|
|
647
644
|
@property
|
648
645
|
def target_string(self) -> str:
|
@@ -726,7 +723,7 @@ class OnConflict:
|
|
726
723
|
parent class.
|
727
724
|
"""
|
728
725
|
|
729
|
-
on_conflict_items:
|
726
|
+
on_conflict_items: list[OnConflictItem] = field(default_factory=list)
|
730
727
|
|
731
728
|
@property
|
732
729
|
def querystring(self) -> QueryString:
|
@@ -757,14 +754,12 @@ class OnConflictDelegate:
|
|
757
754
|
|
758
755
|
def on_conflict(
|
759
756
|
self,
|
760
|
-
target:
|
761
|
-
action:
|
762
|
-
OnConflictAction,
|
757
|
+
target: Optional[Union[str, Column, tuple[Column, ...]]] = None,
|
758
|
+
action: Union[
|
759
|
+
OnConflictAction, Literal["DO NOTHING", "DO UPDATE"]
|
763
760
|
] = OnConflictAction.do_nothing,
|
764
|
-
values:
|
765
|
-
|
766
|
-
] = None,
|
767
|
-
where: t.Optional[Combinable] = None,
|
761
|
+
values: Optional[Sequence[Union[Column, tuple[Column, Any]]]] = None,
|
762
|
+
where: Optional[Combinable] = None,
|
768
763
|
):
|
769
764
|
action_: OnConflictAction
|
770
765
|
if isinstance(action, OnConflictAction):
|
@@ -806,7 +801,7 @@ class LockRows:
|
|
806
801
|
lock_strength: LockStrength
|
807
802
|
nowait: bool
|
808
803
|
skip_locked: bool
|
809
|
-
of:
|
804
|
+
of: tuple[type[Table], ...]
|
810
805
|
|
811
806
|
def __post_init__(self):
|
812
807
|
if not isinstance(self.lock_strength, LockStrength):
|
@@ -846,13 +841,13 @@ class LockRows:
|
|
846
841
|
@dataclass
|
847
842
|
class LockRowsDelegate:
|
848
843
|
|
849
|
-
_lock_rows:
|
844
|
+
_lock_rows: Optional[LockRows] = None
|
850
845
|
|
851
846
|
def lock_rows(
|
852
847
|
self,
|
853
|
-
lock_strength:
|
848
|
+
lock_strength: Union[
|
854
849
|
LockStrength,
|
855
|
-
|
850
|
+
Literal[
|
856
851
|
"UPDATE",
|
857
852
|
"NO KEY UPDATE",
|
858
853
|
"KEY SHARE",
|
@@ -861,7 +856,7 @@ class LockRowsDelegate:
|
|
861
856
|
] = LockStrength.update,
|
862
857
|
nowait=False,
|
863
858
|
skip_locked=False,
|
864
|
-
of:
|
859
|
+
of: tuple[type[Table], ...] = (),
|
865
860
|
):
|
866
861
|
lock_strength_: LockStrength
|
867
862
|
if isinstance(lock_strength, LockStrength):
|
piccolo/query/operators/json.py
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
4
4
|
|
5
5
|
from piccolo.querystring import QueryString
|
6
6
|
from piccolo.utils.encoding import dump_json
|
7
7
|
|
8
|
-
if
|
8
|
+
if TYPE_CHECKING:
|
9
9
|
from piccolo.columns.column_types import JSON
|
10
10
|
|
11
11
|
|
12
12
|
class JSONQueryString(QueryString):
|
13
13
|
|
14
|
-
def clean_value(self, value:
|
14
|
+
def clean_value(self, value: Any):
|
15
15
|
if not isinstance(value, (str, QueryString)):
|
16
16
|
value = dump_json(value)
|
17
17
|
return value
|
@@ -42,9 +42,9 @@ class GetChildElement(JSONQueryString):
|
|
42
42
|
|
43
43
|
def __init__(
|
44
44
|
self,
|
45
|
-
identifier:
|
46
|
-
key:
|
47
|
-
alias:
|
45
|
+
identifier: Union[JSON, QueryString],
|
46
|
+
key: Union[str, int, QueryString],
|
47
|
+
alias: Optional[str] = None,
|
48
48
|
):
|
49
49
|
if isinstance(key, int):
|
50
50
|
# asyncpg only accepts integer keys if we explicitly mark it as an
|
@@ -53,7 +53,7 @@ class GetChildElement(JSONQueryString):
|
|
53
53
|
|
54
54
|
super().__init__("{} -> {}", identifier, key, alias=alias)
|
55
55
|
|
56
|
-
def arrow(self, key:
|
56
|
+
def arrow(self, key: Union[str, int, QueryString]) -> GetChildElement:
|
57
57
|
"""
|
58
58
|
This allows you to drill multiple levels deep into a JSON object if
|
59
59
|
needed.
|
@@ -77,7 +77,7 @@ class GetChildElement(JSONQueryString):
|
|
77
77
|
return GetChildElement(identifier=self, key=key, alias=self._alias)
|
78
78
|
|
79
79
|
def __getitem__(
|
80
|
-
self, value:
|
80
|
+
self, value: Union[str, int, QueryString]
|
81
81
|
) -> GetChildElement:
|
82
82
|
return GetChildElement(identifier=self, key=value, alias=self._alias)
|
83
83
|
|
@@ -94,9 +94,9 @@ class GetElementFromPath(JSONQueryString):
|
|
94
94
|
|
95
95
|
def __init__(
|
96
96
|
self,
|
97
|
-
identifier:
|
98
|
-
path:
|
99
|
-
alias:
|
97
|
+
identifier: Union[JSON, QueryString],
|
98
|
+
path: list[Union[str, int]],
|
99
|
+
alias: Optional[str] = None,
|
100
100
|
):
|
101
101
|
"""
|
102
102
|
:param path:
|
piccolo/query/proxy.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import inspect
|
2
|
-
|
2
|
+
from collections.abc import Generator
|
3
|
+
from typing import Generic, Optional, TypeVar
|
3
4
|
|
4
5
|
from typing_extensions import Protocol
|
5
6
|
|
@@ -8,22 +9,20 @@ from piccolo.utils.sync import run_sync
|
|
8
9
|
|
9
10
|
|
10
11
|
class Runnable(Protocol):
|
11
|
-
async def run(
|
12
|
-
self, node: t.Optional[str] = None, in_pool: bool = True
|
13
|
-
): ...
|
12
|
+
async def run(self, node: Optional[str] = None, in_pool: bool = True): ...
|
14
13
|
|
15
14
|
|
16
|
-
QueryType =
|
17
|
-
ResponseType =
|
15
|
+
QueryType = TypeVar("QueryType", bound=Runnable)
|
16
|
+
ResponseType = TypeVar("ResponseType")
|
18
17
|
|
19
18
|
|
20
|
-
class Proxy(
|
19
|
+
class Proxy(Generic[QueryType, ResponseType]):
|
21
20
|
def __init__(self, query: QueryType):
|
22
21
|
self.query = query
|
23
22
|
|
24
23
|
async def run(
|
25
24
|
self,
|
26
|
-
node:
|
25
|
+
node: Optional[str] = None,
|
27
26
|
in_pool: bool = True,
|
28
27
|
) -> ResponseType:
|
29
28
|
return await self.query.run(node=node, in_pool=in_pool)
|
@@ -33,7 +32,7 @@ class Proxy(t.Generic[QueryType, ResponseType]):
|
|
33
32
|
|
34
33
|
def __await__(
|
35
34
|
self,
|
36
|
-
) ->
|
35
|
+
) -> Generator[None, None, ResponseType]:
|
37
36
|
"""
|
38
37
|
If the user doesn't explicity call .run(), proxy to it as a
|
39
38
|
convenience.
|
piccolo/querystring.py
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import typing as t
|
4
3
|
from abc import ABCMeta, abstractmethod
|
4
|
+
from collections.abc import Sequence
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from datetime import datetime
|
7
7
|
from importlib.util import find_spec
|
8
8
|
from string import Formatter
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional
|
9
10
|
|
10
|
-
if
|
11
|
-
from piccolo.table import Table
|
11
|
+
if TYPE_CHECKING: # pragma: no cover
|
12
12
|
from piccolo.columns import Column
|
13
|
+
from piccolo.table import Table
|
13
14
|
|
14
15
|
from uuid import UUID
|
15
16
|
|
@@ -26,7 +27,7 @@ class Selectable(metaclass=ABCMeta):
|
|
26
27
|
|
27
28
|
__slots__ = ("_alias",)
|
28
29
|
|
29
|
-
_alias:
|
30
|
+
_alias: Optional[str]
|
30
31
|
|
31
32
|
@abstractmethod
|
32
33
|
def get_select_string(
|
@@ -74,10 +75,10 @@ class QueryString(Selectable):
|
|
74
75
|
def __init__(
|
75
76
|
self,
|
76
77
|
template: str,
|
77
|
-
*args:
|
78
|
+
*args: Any,
|
78
79
|
query_type: str = "generic",
|
79
|
-
table:
|
80
|
-
alias:
|
80
|
+
table: Optional[type[Table]] = None,
|
81
|
+
alias: Optional[str] = None,
|
81
82
|
) -> None:
|
82
83
|
"""
|
83
84
|
:param template:
|
@@ -99,15 +100,13 @@ class QueryString(Selectable):
|
|
99
100
|
self.template = template
|
100
101
|
self.query_type = query_type
|
101
102
|
self.table = table
|
102
|
-
self._frozen_compiled_strings:
|
103
|
-
t.Tuple[str, t.List[t.Any]]
|
104
|
-
] = None
|
103
|
+
self._frozen_compiled_strings: Optional[tuple[str, list[Any]]] = None
|
105
104
|
self._alias = alias
|
106
105
|
self.args, self.columns = self.process_args(args)
|
107
106
|
|
108
107
|
def process_args(
|
109
|
-
self, args:
|
110
|
-
) ->
|
108
|
+
self, args: Sequence[Any]
|
109
|
+
) -> tuple[Sequence[Any], Sequence[Column]]:
|
111
110
|
"""
|
112
111
|
If a Column is passed in, we convert it to the name of the column
|
113
112
|
(including joins).
|
@@ -168,8 +167,8 @@ class QueryString(Selectable):
|
|
168
167
|
def bundle(
|
169
168
|
self,
|
170
169
|
start_index: int = 1,
|
171
|
-
bundled:
|
172
|
-
combined_args:
|
170
|
+
bundled: Optional[list[Fragment]] = None,
|
171
|
+
combined_args: Optional[list] = None,
|
173
172
|
):
|
174
173
|
# Split up the string, separating by {}.
|
175
174
|
fragments = [
|
@@ -206,7 +205,7 @@ class QueryString(Selectable):
|
|
206
205
|
|
207
206
|
def compile_string(
|
208
207
|
self, engine_type: str = "postgres"
|
209
|
-
) ->
|
208
|
+
) -> tuple[str, list[Any]]:
|
210
209
|
"""
|
211
210
|
Compiles the template ready for the engine - keeping the arguments
|
212
211
|
separate from the template.
|
piccolo/schema.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import abc
|
4
|
-
|
4
|
+
from typing import Optional, cast
|
5
5
|
|
6
6
|
from piccolo.engine.base import Engine
|
7
7
|
from piccolo.engine.finder import engine_finder
|
@@ -110,7 +110,7 @@ class MoveTable(SchemaDDLBase):
|
|
110
110
|
table_name: str,
|
111
111
|
new_schema: str,
|
112
112
|
db: Engine,
|
113
|
-
current_schema:
|
113
|
+
current_schema: Optional[str] = None,
|
114
114
|
):
|
115
115
|
self.table_name = table_name
|
116
116
|
self.current_schema = current_schema
|
@@ -131,9 +131,9 @@ class ListTables:
|
|
131
131
|
self.db = db
|
132
132
|
self.schema_name = schema_name
|
133
133
|
|
134
|
-
async def run(self) ->
|
135
|
-
response =
|
136
|
-
|
134
|
+
async def run(self) -> list[str]:
|
135
|
+
response = cast(
|
136
|
+
list[dict],
|
137
137
|
await self.db.run_querystring(
|
138
138
|
QueryString(
|
139
139
|
"""
|
@@ -158,9 +158,9 @@ class ListSchemas:
|
|
158
158
|
def __init__(self, db: Engine):
|
159
159
|
self.db = db
|
160
160
|
|
161
|
-
async def run(self) ->
|
162
|
-
response =
|
163
|
-
|
161
|
+
async def run(self) -> list[str]:
|
162
|
+
response = cast(
|
163
|
+
list[dict],
|
164
164
|
await self.db.run_querystring(
|
165
165
|
QueryString(
|
166
166
|
"SELECT schema_name FROM information_schema.schemata"
|
@@ -177,7 +177,7 @@ class ListSchemas:
|
|
177
177
|
|
178
178
|
|
179
179
|
class SchemaManager:
|
180
|
-
def __init__(self, db:
|
180
|
+
def __init__(self, db: Optional[Engine] = None):
|
181
181
|
"""
|
182
182
|
A useful utility class for interacting with schemas.
|
183
183
|
|
@@ -267,7 +267,7 @@ class SchemaManager:
|
|
267
267
|
self,
|
268
268
|
table_name: str,
|
269
269
|
new_schema: str,
|
270
|
-
current_schema:
|
270
|
+
current_schema: Optional[str] = None,
|
271
271
|
) -> MoveTable:
|
272
272
|
"""
|
273
273
|
Moves a table to a different schema::
|