piccolo 1.27.0__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.0.dist-info → piccolo-1.28.0.dist-info}/METADATA +1 -1
- {piccolo-1.27.0.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.0.dist-info → piccolo-1.28.0.dist-info}/WHEEL +0 -0
- {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/licenses/LICENSE +0 -0
- {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/top_level.txt +0 -0
piccolo/table.py
CHANGED
@@ -3,9 +3,10 @@ from __future__ import annotations
|
|
3
3
|
import inspect
|
4
4
|
import itertools
|
5
5
|
import types
|
6
|
-
import typing as t
|
7
6
|
import warnings
|
7
|
+
from collections.abc import Sequence
|
8
8
|
from dataclasses import dataclass, field
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
|
9
10
|
|
10
11
|
from piccolo.columns import Column
|
11
12
|
from piccolo.columns.column_types import (
|
@@ -55,7 +56,7 @@ from piccolo.utils.sql_values import convert_to_sql_value
|
|
55
56
|
from piccolo.utils.sync import run_sync
|
56
57
|
from piccolo.utils.warnings import colored_warning
|
57
58
|
|
58
|
-
if
|
59
|
+
if TYPE_CHECKING: # pragma: no cover
|
59
60
|
from piccolo.querystring import Selectable
|
60
61
|
|
61
62
|
PROTECTED_TABLENAMES = ("user",)
|
@@ -65,7 +66,7 @@ TABLENAME_WARNING = (
|
|
65
66
|
)
|
66
67
|
|
67
68
|
|
68
|
-
TABLE_REGISTRY:
|
69
|
+
TABLE_REGISTRY: list[type[Table]] = []
|
69
70
|
|
70
71
|
|
71
72
|
@dataclass
|
@@ -75,26 +76,26 @@ class TableMeta:
|
|
75
76
|
"""
|
76
77
|
|
77
78
|
tablename: str = ""
|
78
|
-
columns:
|
79
|
-
default_columns:
|
80
|
-
non_default_columns:
|
81
|
-
array_columns:
|
82
|
-
email_columns:
|
83
|
-
foreign_key_columns:
|
79
|
+
columns: list[Column] = field(default_factory=list)
|
80
|
+
default_columns: list[Column] = field(default_factory=list)
|
81
|
+
non_default_columns: list[Column] = field(default_factory=list)
|
82
|
+
array_columns: list[Array] = field(default_factory=list)
|
83
|
+
email_columns: list[Email] = field(default_factory=list)
|
84
|
+
foreign_key_columns: list[ForeignKey] = field(default_factory=list)
|
84
85
|
primary_key: Column = field(default_factory=Column)
|
85
|
-
json_columns:
|
86
|
-
secret_columns:
|
87
|
-
auto_update_columns:
|
88
|
-
tags:
|
89
|
-
help_text:
|
90
|
-
_db:
|
91
|
-
m2m_relationships:
|
92
|
-
schema:
|
86
|
+
json_columns: list[Union[JSON, JSONB]] = field(default_factory=list)
|
87
|
+
secret_columns: list[Secret] = field(default_factory=list)
|
88
|
+
auto_update_columns: list[Column] = field(default_factory=list)
|
89
|
+
tags: list[str] = field(default_factory=list)
|
90
|
+
help_text: Optional[str] = None
|
91
|
+
_db: Optional[Engine] = None
|
92
|
+
m2m_relationships: list[M2M] = field(default_factory=list)
|
93
|
+
schema: Optional[str] = None
|
93
94
|
|
94
95
|
# Records reverse foreign key relationships - i.e. when the current table
|
95
96
|
# is the target of a foreign key. Used by external libraries such as
|
96
97
|
# Piccolo API.
|
97
|
-
_foreign_key_references:
|
98
|
+
_foreign_key_references: list[ForeignKey] = field(default_factory=list)
|
98
99
|
|
99
100
|
def get_formatted_tablename(
|
100
101
|
self, include_schema: bool = True, quoted: bool = True
|
@@ -120,8 +121,8 @@ class TableMeta:
|
|
120
121
|
return ".".join(components)
|
121
122
|
|
122
123
|
@property
|
123
|
-
def foreign_key_references(self) ->
|
124
|
-
foreign_keys:
|
124
|
+
def foreign_key_references(self) -> list[ForeignKey]:
|
125
|
+
foreign_keys: list[ForeignKey] = list(self._foreign_key_references)
|
125
126
|
lazy_column_references = LAZY_COLUMN_REFERENCES.for_tablename(
|
126
127
|
tablename=self.tablename
|
127
128
|
)
|
@@ -173,11 +174,11 @@ class TableMeta:
|
|
173
174
|
|
174
175
|
return column_object
|
175
176
|
|
176
|
-
def get_auto_update_values(self) ->
|
177
|
+
def get_auto_update_values(self) -> dict[Column, Any]:
|
177
178
|
"""
|
178
179
|
If columns have ``auto_update`` defined, then we retrieve these values.
|
179
180
|
"""
|
180
|
-
output:
|
181
|
+
output: dict[Column, Any] = {}
|
181
182
|
for column in self.auto_update_columns:
|
182
183
|
value = column._meta.auto_update
|
183
184
|
if callable(value):
|
@@ -203,7 +204,7 @@ class TableMetaclass(type):
|
|
203
204
|
|
204
205
|
# `SessionsBase` is a `Table` subclass:
|
205
206
|
def session_auth(
|
206
|
-
session_table:
|
207
|
+
session_table: type[SessionsBase] = SessionsBase
|
207
208
|
):
|
208
209
|
...
|
209
210
|
|
@@ -227,11 +228,11 @@ class Table(metaclass=TableMetaclass):
|
|
227
228
|
|
228
229
|
def __init_subclass__(
|
229
230
|
cls,
|
230
|
-
tablename:
|
231
|
-
db:
|
232
|
-
tags:
|
233
|
-
help_text:
|
234
|
-
schema:
|
231
|
+
tablename: Optional[str] = None,
|
232
|
+
db: Optional[Engine] = None,
|
233
|
+
tags: Optional[list[str]] = None,
|
234
|
+
help_text: Optional[str] = None,
|
235
|
+
schema: Optional[str] = None,
|
235
236
|
): # sourcery no-metrics
|
236
237
|
"""
|
237
238
|
Automatically populate the _meta, which includes the tablename, and
|
@@ -268,17 +269,17 @@ class Table(metaclass=TableMetaclass):
|
|
268
269
|
if tablename in PROTECTED_TABLENAMES:
|
269
270
|
warnings.warn(TABLENAME_WARNING.format(tablename=tablename))
|
270
271
|
|
271
|
-
columns:
|
272
|
-
default_columns:
|
273
|
-
non_default_columns:
|
274
|
-
array_columns:
|
275
|
-
foreign_key_columns:
|
276
|
-
secret_columns:
|
277
|
-
json_columns:
|
278
|
-
email_columns:
|
279
|
-
auto_update_columns:
|
280
|
-
primary_key:
|
281
|
-
m2m_relationships:
|
272
|
+
columns: list[Column] = []
|
273
|
+
default_columns: list[Column] = []
|
274
|
+
non_default_columns: list[Column] = []
|
275
|
+
array_columns: list[Array] = []
|
276
|
+
foreign_key_columns: list[ForeignKey] = []
|
277
|
+
secret_columns: list[Secret] = []
|
278
|
+
json_columns: list[Union[JSON, JSONB]] = []
|
279
|
+
email_columns: list[Email] = []
|
280
|
+
auto_update_columns: list[Column] = []
|
281
|
+
primary_key: Optional[Column] = None
|
282
|
+
m2m_relationships: list[M2M] = []
|
282
283
|
|
283
284
|
attribute_names = itertools.chain(
|
284
285
|
*[i.__dict__.keys() for i in reversed(cls.__mro__)]
|
@@ -372,7 +373,7 @@ class Table(metaclass=TableMetaclass):
|
|
372
373
|
|
373
374
|
def __init__(
|
374
375
|
self,
|
375
|
-
_data:
|
376
|
+
_data: Optional[dict[Column, Any]] = None,
|
376
377
|
_ignore_missing: bool = False,
|
377
378
|
_exists_in_db: bool = False,
|
378
379
|
**kwargs,
|
@@ -412,7 +413,7 @@ class Table(metaclass=TableMetaclass):
|
|
412
413
|
|
413
414
|
# This is used by get_or_create to indicate to the user whether it
|
414
415
|
# was an existing row or not.
|
415
|
-
self._was_created:
|
416
|
+
self._was_created: Optional[bool] = None
|
416
417
|
|
417
418
|
for column in self._meta.columns:
|
418
419
|
value = _data.get(column, ...)
|
@@ -423,7 +424,7 @@ class Table(metaclass=TableMetaclass):
|
|
423
424
|
|
424
425
|
if value is ...:
|
425
426
|
value = kwargs.pop(
|
426
|
-
|
427
|
+
cast(str, column._meta.db_column_name), ...
|
427
428
|
)
|
428
429
|
|
429
430
|
if value is ...:
|
@@ -456,7 +457,7 @@ class Table(metaclass=TableMetaclass):
|
|
456
457
|
|
457
458
|
@classmethod
|
458
459
|
def from_dict(
|
459
|
-
cls:
|
460
|
+
cls: type[TableInstance], data: dict[str, Any]
|
460
461
|
) -> TableInstance:
|
461
462
|
"""
|
462
463
|
Used when loading fixtures. It can be overriden by subclasses in case
|
@@ -468,8 +469,8 @@ class Table(metaclass=TableMetaclass):
|
|
468
469
|
###########################################################################
|
469
470
|
|
470
471
|
def save(
|
471
|
-
self, columns:
|
472
|
-
) ->
|
472
|
+
self, columns: Optional[Sequence[Union[Column, str]]] = None
|
473
|
+
) -> Union[Insert, Update]:
|
473
474
|
"""
|
474
475
|
A proxy to an insert or update query.
|
475
476
|
|
@@ -504,7 +505,7 @@ class Table(metaclass=TableMetaclass):
|
|
504
505
|
for i in columns
|
505
506
|
]
|
506
507
|
|
507
|
-
values:
|
508
|
+
values: dict[Column, Any] = {
|
508
509
|
i: getattr(self, i._meta.name, None) for i in column_instances
|
509
510
|
}
|
510
511
|
|
@@ -525,9 +526,7 @@ class Table(metaclass=TableMetaclass):
|
|
525
526
|
== getattr(self, self._meta.primary_key._meta.name)
|
526
527
|
)
|
527
528
|
|
528
|
-
def update_self(
|
529
|
-
self, values: t.Dict[t.Union[Column, str], t.Any]
|
530
|
-
) -> UpdateSelf:
|
529
|
+
def update_self(self, values: dict[Union[Column, str], Any]) -> UpdateSelf:
|
531
530
|
"""
|
532
531
|
This allows the user to update a single object - useful when the values
|
533
532
|
are derived from the database in some way.
|
@@ -581,7 +580,7 @@ class Table(metaclass=TableMetaclass):
|
|
581
580
|
|
582
581
|
def refresh(
|
583
582
|
self,
|
584
|
-
columns:
|
583
|
+
columns: Optional[Sequence[Column]] = None,
|
585
584
|
load_json: bool = False,
|
586
585
|
) -> Refresh:
|
587
586
|
"""
|
@@ -611,16 +610,16 @@ class Table(metaclass=TableMetaclass):
|
|
611
610
|
"""
|
612
611
|
return Refresh(instance=self, columns=columns, load_json=load_json)
|
613
612
|
|
614
|
-
@
|
613
|
+
@overload
|
615
614
|
def get_related(
|
616
615
|
self, foreign_key: ForeignKey[ReferencedTable]
|
617
616
|
) -> GetRelated[ReferencedTable]: ...
|
618
617
|
|
619
|
-
@
|
618
|
+
@overload
|
620
619
|
def get_related(self, foreign_key: str) -> GetRelated[Table]: ...
|
621
620
|
|
622
621
|
def get_related(
|
623
|
-
self, foreign_key:
|
622
|
+
self, foreign_key: Union[str, ForeignKey[ReferencedTable]]
|
624
623
|
) -> GetRelated[ReferencedTable]:
|
625
624
|
"""
|
626
625
|
Used to fetch a ``Table`` instance, for the target of a foreign key.
|
@@ -666,7 +665,7 @@ class Table(metaclass=TableMetaclass):
|
|
666
665
|
self,
|
667
666
|
*rows: Table,
|
668
667
|
m2m: M2M,
|
669
|
-
extra_column_values:
|
668
|
+
extra_column_values: dict[Union[Column, str], Any] = {},
|
670
669
|
) -> M2MAddRelated:
|
671
670
|
"""
|
672
671
|
Save the row if it doesn't already exist in the database, and insert
|
@@ -733,7 +732,7 @@ class Table(metaclass=TableMetaclass):
|
|
733
732
|
m2m=m2m,
|
734
733
|
)
|
735
734
|
|
736
|
-
def to_dict(self, *columns: Column) ->
|
735
|
+
def to_dict(self, *columns: Column) -> dict[str, Any]:
|
737
736
|
"""
|
738
737
|
A convenience method which returns a dictionary, mapping column names
|
739
738
|
to values for this table instance.
|
@@ -785,7 +784,7 @@ class Table(metaclass=TableMetaclass):
|
|
785
784
|
)
|
786
785
|
return output
|
787
786
|
|
788
|
-
def __setitem__(self, key: str, value:
|
787
|
+
def __setitem__(self, key: str, value: Any):
|
789
788
|
setattr(self, key, value)
|
790
789
|
|
791
790
|
def __getitem__(self, key: str):
|
@@ -858,8 +857,8 @@ class Table(metaclass=TableMetaclass):
|
|
858
857
|
|
859
858
|
@classmethod
|
860
859
|
def all_related(
|
861
|
-
cls, exclude:
|
862
|
-
) ->
|
860
|
+
cls, exclude: Optional[list[Union[str, ForeignKey]]] = None
|
861
|
+
) -> list[ForeignKey]:
|
863
862
|
"""
|
864
863
|
Used in conjunction with ``objects`` queries. Just as we can use
|
865
864
|
``all_related`` on a ``ForeignKey``, you can also use it for the table
|
@@ -908,8 +907,8 @@ class Table(metaclass=TableMetaclass):
|
|
908
907
|
|
909
908
|
@classmethod
|
910
909
|
def all_columns(
|
911
|
-
cls, exclude:
|
912
|
-
) ->
|
910
|
+
cls, exclude: Optional[Sequence[Union[str, Column]]] = None
|
911
|
+
) -> list[Column]:
|
913
912
|
"""
|
914
913
|
Used in conjunction with ``select`` queries. Just as we can use
|
915
914
|
``all_columns`` to retrieve all of the columns from a related table,
|
@@ -975,7 +974,7 @@ class Table(metaclass=TableMetaclass):
|
|
975
974
|
|
976
975
|
@classmethod
|
977
976
|
def insert(
|
978
|
-
cls:
|
977
|
+
cls: type[TableInstance], *rows: TableInstance
|
979
978
|
) -> Insert[TableInstance]:
|
980
979
|
"""
|
981
980
|
Insert rows into the database.
|
@@ -993,7 +992,7 @@ class Table(metaclass=TableMetaclass):
|
|
993
992
|
return query
|
994
993
|
|
995
994
|
@classmethod
|
996
|
-
def raw(cls, sql: str, *args:
|
995
|
+
def raw(cls, sql: str, *args: Any) -> Raw:
|
997
996
|
"""
|
998
997
|
Execute raw SQL queries on the underlying engine - use with caution!
|
999
998
|
|
@@ -1012,8 +1011,8 @@ class Table(metaclass=TableMetaclass):
|
|
1012
1011
|
|
1013
1012
|
@classmethod
|
1014
1013
|
def _process_column_args(
|
1015
|
-
cls, *columns:
|
1016
|
-
) ->
|
1014
|
+
cls, *columns: Union[Selectable, str]
|
1015
|
+
) -> Sequence[Selectable]:
|
1017
1016
|
"""
|
1018
1017
|
Users can specify some column arguments as either Column instances, or
|
1019
1018
|
as strings representing the column name, for convenience.
|
@@ -1030,7 +1029,7 @@ class Table(metaclass=TableMetaclass):
|
|
1030
1029
|
|
1031
1030
|
@classmethod
|
1032
1031
|
def select(
|
1033
|
-
cls, *columns:
|
1032
|
+
cls, *columns: Union[Selectable, str], exclude_secrets=False
|
1034
1033
|
) -> Select:
|
1035
1034
|
"""
|
1036
1035
|
Get data in the form of a list of dictionaries, with each dictionary
|
@@ -1109,8 +1108,8 @@ class Table(metaclass=TableMetaclass):
|
|
1109
1108
|
|
1110
1109
|
@classmethod
|
1111
1110
|
def objects(
|
1112
|
-
cls:
|
1113
|
-
*prefetch:
|
1111
|
+
cls: type[TableInstance],
|
1112
|
+
*prefetch: Union[ForeignKey, list[ForeignKey]],
|
1114
1113
|
) -> Objects[TableInstance]:
|
1115
1114
|
"""
|
1116
1115
|
Returns a list of table instances (each representing a row), which you
|
@@ -1151,8 +1150,8 @@ class Table(metaclass=TableMetaclass):
|
|
1151
1150
|
@classmethod
|
1152
1151
|
def count(
|
1153
1152
|
cls,
|
1154
|
-
column:
|
1155
|
-
distinct:
|
1153
|
+
column: Optional[Column] = None,
|
1154
|
+
distinct: Optional[Sequence[Column]] = None,
|
1156
1155
|
) -> Count:
|
1157
1156
|
"""
|
1158
1157
|
Count the number of matching rows::
|
@@ -1224,7 +1223,7 @@ class Table(metaclass=TableMetaclass):
|
|
1224
1223
|
@classmethod
|
1225
1224
|
def update(
|
1226
1225
|
cls,
|
1227
|
-
values:
|
1226
|
+
values: Optional[dict[Union[Column, str], Any]] = None,
|
1228
1227
|
force: bool = False,
|
1229
1228
|
use_auto_update: bool = True,
|
1230
1229
|
**kwargs,
|
@@ -1288,7 +1287,7 @@ class Table(metaclass=TableMetaclass):
|
|
1288
1287
|
@classmethod
|
1289
1288
|
def create_index(
|
1290
1289
|
cls,
|
1291
|
-
columns:
|
1290
|
+
columns: Union[list[Column], list[str]],
|
1292
1291
|
method: IndexMethod = IndexMethod.btree,
|
1293
1292
|
if_not_exists: bool = False,
|
1294
1293
|
) -> CreateIndex:
|
@@ -1311,7 +1310,7 @@ class Table(metaclass=TableMetaclass):
|
|
1311
1310
|
@classmethod
|
1312
1311
|
def drop_index(
|
1313
1312
|
cls,
|
1314
|
-
columns:
|
1313
|
+
columns: Union[list[Column], list[str]],
|
1315
1314
|
if_exists: bool = True,
|
1316
1315
|
) -> DropIndex:
|
1317
1316
|
"""
|
@@ -1328,7 +1327,7 @@ class Table(metaclass=TableMetaclass):
|
|
1328
1327
|
###########################################################################
|
1329
1328
|
|
1330
1329
|
@classmethod
|
1331
|
-
def _get_index_name(cls, column_names:
|
1330
|
+
def _get_index_name(cls, column_names: list[str]) -> str:
|
1332
1331
|
"""
|
1333
1332
|
Generates an index name from the table name and column names.
|
1334
1333
|
"""
|
@@ -1338,7 +1337,7 @@ class Table(metaclass=TableMetaclass):
|
|
1338
1337
|
|
1339
1338
|
@classmethod
|
1340
1339
|
def _table_str(
|
1341
|
-
cls, abbreviated=False, excluded_params:
|
1340
|
+
cls, abbreviated=False, excluded_params: Optional[list[str]] = None
|
1342
1341
|
):
|
1343
1342
|
"""
|
1344
1343
|
Returns a basic string representation of the table and its columns.
|
@@ -1357,7 +1356,7 @@ class Table(metaclass=TableMetaclass):
|
|
1357
1356
|
spacer = "\n "
|
1358
1357
|
columns = []
|
1359
1358
|
for col in cls._meta.columns:
|
1360
|
-
params:
|
1359
|
+
params: list[str] = []
|
1361
1360
|
for key, value in col._meta.params.items():
|
1362
1361
|
if key in excluded_params:
|
1363
1362
|
continue
|
@@ -1392,10 +1391,10 @@ class Table(metaclass=TableMetaclass):
|
|
1392
1391
|
|
1393
1392
|
def create_table_class(
|
1394
1393
|
class_name: str,
|
1395
|
-
bases:
|
1396
|
-
class_kwargs:
|
1397
|
-
class_members:
|
1398
|
-
) ->
|
1394
|
+
bases: tuple[type] = (Table,),
|
1395
|
+
class_kwargs: dict[str, Any] = {},
|
1396
|
+
class_members: dict[str, Any] = {},
|
1397
|
+
) -> type[Table]:
|
1399
1398
|
"""
|
1400
1399
|
Used to dynamically create ``Table``subclasses at runtime. Most users
|
1401
1400
|
will not require this. It's mostly used internally for Piccolo's
|
@@ -1411,8 +1410,8 @@ def create_table_class(
|
|
1411
1410
|
For example, `{'my_column': Varchar()}`.
|
1412
1411
|
|
1413
1412
|
"""
|
1414
|
-
return
|
1415
|
-
|
1413
|
+
return cast(
|
1414
|
+
type[Table],
|
1416
1415
|
types.new_class(
|
1417
1416
|
name=class_name,
|
1418
1417
|
bases=bases,
|
@@ -1427,7 +1426,7 @@ def create_table_class(
|
|
1427
1426
|
|
1428
1427
|
|
1429
1428
|
async def create_db_tables(
|
1430
|
-
*tables:
|
1429
|
+
*tables: type[Table], if_not_exists: bool = False
|
1431
1430
|
) -> None:
|
1432
1431
|
"""
|
1433
1432
|
Creates the database table for each ``Table`` class passed in. The tables
|
@@ -1458,7 +1457,7 @@ async def create_db_tables(
|
|
1458
1457
|
|
1459
1458
|
|
1460
1459
|
def create_db_tables_sync(
|
1461
|
-
*tables:
|
1460
|
+
*tables: type[Table], if_not_exists: bool = False
|
1462
1461
|
) -> None:
|
1463
1462
|
"""
|
1464
1463
|
A sync wrapper around :func:`create_db_tables`.
|
@@ -1466,7 +1465,7 @@ def create_db_tables_sync(
|
|
1466
1465
|
run_sync(create_db_tables(*tables, if_not_exists=if_not_exists))
|
1467
1466
|
|
1468
1467
|
|
1469
|
-
def create_tables(*tables:
|
1468
|
+
def create_tables(*tables: type[Table], if_not_exists: bool = False) -> None:
|
1470
1469
|
"""
|
1471
1470
|
This original implementation has been replaced, because it was synchronous,
|
1472
1471
|
and felt at odds with the rest of the Piccolo codebase which is async
|
@@ -1485,7 +1484,7 @@ def create_tables(*tables: t.Type[Table], if_not_exists: bool = False) -> None:
|
|
1485
1484
|
return create_db_tables_sync(*tables, if_not_exists=if_not_exists)
|
1486
1485
|
|
1487
1486
|
|
1488
|
-
async def drop_db_tables(*tables:
|
1487
|
+
async def drop_db_tables(*tables: type[Table]) -> None:
|
1489
1488
|
"""
|
1490
1489
|
Drops the database table for each ``Table`` class passed in. The tables
|
1491
1490
|
are dropped in the correct order, based on their foreign keys.
|
@@ -1518,14 +1517,14 @@ async def drop_db_tables(*tables: t.Type[Table]) -> None:
|
|
1518
1517
|
await atomic.run()
|
1519
1518
|
|
1520
1519
|
|
1521
|
-
def drop_db_tables_sync(*tables:
|
1520
|
+
def drop_db_tables_sync(*tables: type[Table]) -> None:
|
1522
1521
|
"""
|
1523
1522
|
A sync wrapper around :func:`drop_db_tables`.
|
1524
1523
|
"""
|
1525
1524
|
run_sync(drop_db_tables(*tables))
|
1526
1525
|
|
1527
1526
|
|
1528
|
-
def drop_tables(*tables:
|
1527
|
+
def drop_tables(*tables: type[Table]) -> None:
|
1529
1528
|
"""
|
1530
1529
|
This original implementation has been replaced, because it was synchronous,
|
1531
1530
|
and felt at odds with the rest of the Piccolo codebase which is async
|
@@ -1548,8 +1547,8 @@ def drop_tables(*tables: t.Type[Table]) -> None:
|
|
1548
1547
|
|
1549
1548
|
|
1550
1549
|
def sort_table_classes(
|
1551
|
-
table_classes:
|
1552
|
-
) ->
|
1550
|
+
table_classes: list[type[Table]],
|
1551
|
+
) -> list[type[Table]]:
|
1553
1552
|
"""
|
1554
1553
|
Sort the table classes based on their foreign keys, so they can be created
|
1555
1554
|
in the correct order.
|
@@ -1564,7 +1563,7 @@ def sort_table_classes(
|
|
1564
1563
|
sorter = TopologicalSorter(graph)
|
1565
1564
|
ordered_tablenames = tuple(sorter.static_order())
|
1566
1565
|
|
1567
|
-
output:
|
1566
|
+
output: list[type[Table]] = []
|
1568
1567
|
for tablename in ordered_tablenames:
|
1569
1568
|
table_class = table_class_dict.get(tablename)
|
1570
1569
|
if table_class is not None:
|
@@ -1574,10 +1573,10 @@ def sort_table_classes(
|
|
1574
1573
|
|
1575
1574
|
|
1576
1575
|
def _get_graph(
|
1577
|
-
table_classes:
|
1576
|
+
table_classes: list[type[Table]],
|
1578
1577
|
iterations: int = 0,
|
1579
1578
|
max_iterations: int = 5,
|
1580
|
-
) ->
|
1579
|
+
) -> dict[str, set[str]]:
|
1581
1580
|
"""
|
1582
1581
|
Analyses the tables based on their foreign keys, and returns a data
|
1583
1582
|
structure like:
|
@@ -1590,13 +1589,13 @@ def _get_graph(
|
|
1590
1589
|
to it via a foreign key.
|
1591
1590
|
|
1592
1591
|
"""
|
1593
|
-
output:
|
1592
|
+
output: dict[str, set[str]] = {}
|
1594
1593
|
|
1595
1594
|
if iterations >= max_iterations:
|
1596
1595
|
return output
|
1597
1596
|
|
1598
1597
|
for table_class in table_classes:
|
1599
|
-
dependents:
|
1598
|
+
dependents: set[str] = set()
|
1600
1599
|
for fk in table_class._meta.foreign_key_columns:
|
1601
1600
|
referenced_table = fk._foreign_key_meta.resolved_references
|
1602
1601
|
|
piccolo/table_reflection.py
CHANGED
@@ -4,8 +4,8 @@ tables.
|
|
4
4
|
"""
|
5
5
|
|
6
6
|
import asyncio
|
7
|
-
import typing as t
|
8
7
|
from dataclasses import dataclass
|
8
|
+
from typing import Any, Optional, Union
|
9
9
|
|
10
10
|
from piccolo.apps.schema.commands.generate import get_output_schema
|
11
11
|
from piccolo.engine import engine_finder
|
@@ -58,7 +58,7 @@ class Singleton(type):
|
|
58
58
|
A metaclass that creates a Singleton base class when called.
|
59
59
|
"""
|
60
60
|
|
61
|
-
_instances:
|
61
|
+
_instances: dict = {}
|
62
62
|
|
63
63
|
def __call__(cls, *args, **kwargs):
|
64
64
|
if cls not in cls._instances:
|
@@ -80,7 +80,7 @@ class TableStorage(metaclass=Singleton):
|
|
80
80
|
works with Postgres.
|
81
81
|
"""
|
82
82
|
|
83
|
-
def __init__(self, engine:
|
83
|
+
def __init__(self, engine: Optional[Engine] = None):
|
84
84
|
"""
|
85
85
|
:param engine:
|
86
86
|
Which engine to use to make the database queries. If not specified,
|
@@ -89,13 +89,13 @@ class TableStorage(metaclass=Singleton):
|
|
89
89
|
"""
|
90
90
|
self.engine = engine or engine_finder()
|
91
91
|
self.tables = ImmutableDict()
|
92
|
-
self._schema_tables:
|
92
|
+
self._schema_tables: dict[str, list[str]] = {}
|
93
93
|
|
94
94
|
async def reflect(
|
95
95
|
self,
|
96
96
|
schema_name: str = "public",
|
97
|
-
include:
|
98
|
-
exclude:
|
97
|
+
include: Union[list[str], str, None] = None,
|
98
|
+
exclude: Union[list[str], str, None] = None,
|
99
99
|
keep_existing: bool = False,
|
100
100
|
) -> None:
|
101
101
|
"""
|
@@ -154,7 +154,7 @@ class TableStorage(metaclass=Singleton):
|
|
154
154
|
dict.clear(self.tables)
|
155
155
|
self._schema_tables.clear()
|
156
156
|
|
157
|
-
async def get_table(self, tablename: str) ->
|
157
|
+
async def get_table(self, tablename: str) -> Optional[type[Table]]:
|
158
158
|
"""
|
159
159
|
Returns the ``Table`` class if it exists. If the table is not present
|
160
160
|
in ``TableStorage``, it will try to reflect it.
|
@@ -177,7 +177,7 @@ class TableStorage(metaclass=Singleton):
|
|
177
177
|
table_class = self.tables.get(tablename)
|
178
178
|
return table_class
|
179
179
|
|
180
|
-
async def _add_table(self, schema_name: str, table:
|
180
|
+
async def _add_table(self, schema_name: str, table: type[Table]) -> None:
|
181
181
|
if issubclass(table, Table):
|
182
182
|
table_name = self._get_table_name(
|
183
183
|
table._meta.tablename, schema_name
|
@@ -229,7 +229,7 @@ class TableStorage(metaclass=Singleton):
|
|
229
229
|
raise ValueError("Couldn't find schema name.")
|
230
230
|
|
231
231
|
@staticmethod
|
232
|
-
def _to_list(value:
|
232
|
+
def _to_list(value: Any) -> list:
|
233
233
|
if isinstance(value, list):
|
234
234
|
return value
|
235
235
|
elif isinstance(value, (tuple, set)):
|
piccolo/testing/model_builder.py
CHANGED
@@ -2,8 +2,9 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import datetime
|
4
4
|
import json
|
5
|
-
|
5
|
+
from collections.abc import Callable
|
6
6
|
from decimal import Decimal
|
7
|
+
from typing import Any, Optional, Union, cast
|
7
8
|
from uuid import UUID
|
8
9
|
|
9
10
|
from piccolo.columns import JSON, JSONB, Array, Column, ForeignKey
|
@@ -13,7 +14,7 @@ from piccolo.utils.sync import run_sync
|
|
13
14
|
|
14
15
|
|
15
16
|
class ModelBuilder:
|
16
|
-
__DEFAULT_MAPPER:
|
17
|
+
__DEFAULT_MAPPER: dict[type, Callable] = {
|
17
18
|
bool: RandomBuilder.next_bool,
|
18
19
|
bytes: RandomBuilder.next_bytes,
|
19
20
|
datetime.date: RandomBuilder.next_date,
|
@@ -29,8 +30,8 @@ class ModelBuilder:
|
|
29
30
|
@classmethod
|
30
31
|
async def build(
|
31
32
|
cls,
|
32
|
-
table_class:
|
33
|
-
defaults:
|
33
|
+
table_class: type[TableInstance],
|
34
|
+
defaults: Optional[dict[Union[Column, str], Any]] = None,
|
34
35
|
persist: bool = True,
|
35
36
|
minimal: bool = False,
|
36
37
|
) -> TableInstance:
|
@@ -80,8 +81,8 @@ class ModelBuilder:
|
|
80
81
|
@classmethod
|
81
82
|
def build_sync(
|
82
83
|
cls,
|
83
|
-
table_class:
|
84
|
-
defaults:
|
84
|
+
table_class: type[TableInstance],
|
85
|
+
defaults: Optional[dict[Union[Column, str], Any]] = None,
|
85
86
|
persist: bool = True,
|
86
87
|
minimal: bool = False,
|
87
88
|
) -> TableInstance:
|
@@ -100,8 +101,8 @@ class ModelBuilder:
|
|
100
101
|
@classmethod
|
101
102
|
async def _build(
|
102
103
|
cls,
|
103
|
-
table_class:
|
104
|
-
defaults:
|
104
|
+
table_class: type[TableInstance],
|
105
|
+
defaults: Optional[dict[Union[Column, str], Any]] = None,
|
105
106
|
minimal: bool = False,
|
106
107
|
persist: bool = True,
|
107
108
|
) -> TableInstance:
|
@@ -151,7 +152,7 @@ class ModelBuilder:
|
|
151
152
|
return model
|
152
153
|
|
153
154
|
@classmethod
|
154
|
-
def _randomize_attribute(cls, column: Column) ->
|
155
|
+
def _randomize_attribute(cls, column: Column) -> Any:
|
155
156
|
"""
|
156
157
|
Generate a random value for a column and apply formatting.
|
157
158
|
|
@@ -159,7 +160,7 @@ class ModelBuilder:
|
|
159
160
|
Column class to randomize.
|
160
161
|
|
161
162
|
"""
|
162
|
-
random_value:
|
163
|
+
random_value: Any
|
163
164
|
if column.value_type == Decimal:
|
164
165
|
precision, scale = column._meta.params["digits"] or (4, 2)
|
165
166
|
random_value = RandomBuilder.next_float(
|
@@ -170,7 +171,7 @@ class ModelBuilder:
|
|
170
171
|
random_value = RandomBuilder.next_datetime(tz_aware=tz_aware)
|
171
172
|
elif column.value_type == list:
|
172
173
|
length = RandomBuilder.next_int(maximum=10)
|
173
|
-
base_type =
|
174
|
+
base_type = cast(Array, column).base_column.value_type
|
174
175
|
random_value = [
|
175
176
|
cls.__DEFAULT_MAPPER[base_type]() for _ in range(length)
|
176
177
|
]
|