piccolo 1.27.1__py3-none-any.whl → 1.29.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 +2 -3
- piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja +57 -29
- piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja +48 -21
- piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja +63 -8
- piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja +51 -24
- piccolo/apps/asgi/commands/templates/app/_litestar_app.py.jinja +34 -10
- piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja +38 -15
- piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja +34 -11
- 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 +72 -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 +299 -177
- 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 +105 -98
- piccolo/table_reflection.py +9 -9
- piccolo/testing/model_builder.py +16 -13
- piccolo/testing/random_builder.py +14 -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.29.0.dist-info}/METADATA +1 -1
- {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/RECORD +132 -131
- 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 +20 -49
- 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_metaclass.py +7 -3
- tests/table/test_refresh.py +2 -2
- tests/table/test_select.py +58 -0
- tests/table/test_str.py +30 -22
- tests/table/test_update.py +18 -3
- tests/testing/test_model_builder.py +1 -2
- tests/testing/test_random_builder.py +5 -0
- tests/utils/test_pydantic.py +152 -134
- tests/utils/test_table_reflection.py +1 -2
- {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/WHEEL +0 -0
- {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/licenses/LICENSE +0 -0
- {piccolo-1.27.1.dist-info → piccolo-1.29.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 (
|
@@ -15,7 +16,6 @@ from piccolo.columns.column_types import (
|
|
15
16
|
Email,
|
16
17
|
ForeignKey,
|
17
18
|
ReferencedTable,
|
18
|
-
Secret,
|
19
19
|
Serial,
|
20
20
|
)
|
21
21
|
from piccolo.columns.defaults.base import Default
|
@@ -55,7 +55,7 @@ from piccolo.utils.sql_values import convert_to_sql_value
|
|
55
55
|
from piccolo.utils.sync import run_sync
|
56
56
|
from piccolo.utils.warnings import colored_warning
|
57
57
|
|
58
|
-
if
|
58
|
+
if TYPE_CHECKING: # pragma: no cover
|
59
59
|
from piccolo.querystring import Selectable
|
60
60
|
|
61
61
|
PROTECTED_TABLENAMES = ("user",)
|
@@ -65,7 +65,7 @@ TABLENAME_WARNING = (
|
|
65
65
|
)
|
66
66
|
|
67
67
|
|
68
|
-
TABLE_REGISTRY:
|
68
|
+
TABLE_REGISTRY: list[type[Table]] = []
|
69
69
|
|
70
70
|
|
71
71
|
@dataclass
|
@@ -75,26 +75,26 @@ class TableMeta:
|
|
75
75
|
"""
|
76
76
|
|
77
77
|
tablename: str = ""
|
78
|
-
columns:
|
79
|
-
default_columns:
|
80
|
-
non_default_columns:
|
81
|
-
array_columns:
|
82
|
-
email_columns:
|
83
|
-
foreign_key_columns:
|
78
|
+
columns: list[Column] = field(default_factory=list)
|
79
|
+
default_columns: list[Column] = field(default_factory=list)
|
80
|
+
non_default_columns: list[Column] = field(default_factory=list)
|
81
|
+
array_columns: list[Array] = field(default_factory=list)
|
82
|
+
email_columns: list[Email] = field(default_factory=list)
|
83
|
+
foreign_key_columns: list[ForeignKey] = field(default_factory=list)
|
84
84
|
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:
|
85
|
+
json_columns: list[Union[JSON, JSONB]] = field(default_factory=list)
|
86
|
+
secret_columns: list[Column] = field(default_factory=list)
|
87
|
+
auto_update_columns: list[Column] = field(default_factory=list)
|
88
|
+
tags: list[str] = field(default_factory=list)
|
89
|
+
help_text: Optional[str] = None
|
90
|
+
_db: Optional[Engine] = None
|
91
|
+
m2m_relationships: list[M2M] = field(default_factory=list)
|
92
|
+
schema: Optional[str] = None
|
93
93
|
|
94
94
|
# Records reverse foreign key relationships - i.e. when the current table
|
95
95
|
# is the target of a foreign key. Used by external libraries such as
|
96
96
|
# Piccolo API.
|
97
|
-
_foreign_key_references:
|
97
|
+
_foreign_key_references: list[ForeignKey] = field(default_factory=list)
|
98
98
|
|
99
99
|
def get_formatted_tablename(
|
100
100
|
self, include_schema: bool = True, quoted: bool = True
|
@@ -120,8 +120,8 @@ class TableMeta:
|
|
120
120
|
return ".".join(components)
|
121
121
|
|
122
122
|
@property
|
123
|
-
def foreign_key_references(self) ->
|
124
|
-
foreign_keys:
|
123
|
+
def foreign_key_references(self) -> list[ForeignKey]:
|
124
|
+
foreign_keys: list[ForeignKey] = list(self._foreign_key_references)
|
125
125
|
lazy_column_references = LAZY_COLUMN_REFERENCES.for_tablename(
|
126
126
|
tablename=self.tablename
|
127
127
|
)
|
@@ -173,11 +173,11 @@ class TableMeta:
|
|
173
173
|
|
174
174
|
return column_object
|
175
175
|
|
176
|
-
def get_auto_update_values(self) ->
|
176
|
+
def get_auto_update_values(self) -> dict[Column, Any]:
|
177
177
|
"""
|
178
178
|
If columns have ``auto_update`` defined, then we retrieve these values.
|
179
179
|
"""
|
180
|
-
output:
|
180
|
+
output: dict[Column, Any] = {}
|
181
181
|
for column in self.auto_update_columns:
|
182
182
|
value = column._meta.auto_update
|
183
183
|
if callable(value):
|
@@ -203,7 +203,7 @@ class TableMetaclass(type):
|
|
203
203
|
|
204
204
|
# `SessionsBase` is a `Table` subclass:
|
205
205
|
def session_auth(
|
206
|
-
session_table:
|
206
|
+
session_table: type[SessionsBase] = SessionsBase
|
207
207
|
):
|
208
208
|
...
|
209
209
|
|
@@ -227,11 +227,11 @@ class Table(metaclass=TableMetaclass):
|
|
227
227
|
|
228
228
|
def __init_subclass__(
|
229
229
|
cls,
|
230
|
-
tablename:
|
231
|
-
db:
|
232
|
-
tags:
|
233
|
-
help_text:
|
234
|
-
schema:
|
230
|
+
tablename: Optional[str] = None,
|
231
|
+
db: Optional[Engine] = None,
|
232
|
+
tags: Optional[list[str]] = None,
|
233
|
+
help_text: Optional[str] = None,
|
234
|
+
schema: Optional[str] = None,
|
235
235
|
): # sourcery no-metrics
|
236
236
|
"""
|
237
237
|
Automatically populate the _meta, which includes the tablename, and
|
@@ -268,17 +268,17 @@ class Table(metaclass=TableMetaclass):
|
|
268
268
|
if tablename in PROTECTED_TABLENAMES:
|
269
269
|
warnings.warn(TABLENAME_WARNING.format(tablename=tablename))
|
270
270
|
|
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:
|
271
|
+
columns: list[Column] = []
|
272
|
+
default_columns: list[Column] = []
|
273
|
+
non_default_columns: list[Column] = []
|
274
|
+
array_columns: list[Array] = []
|
275
|
+
foreign_key_columns: list[ForeignKey] = []
|
276
|
+
secret_columns: list[Column] = []
|
277
|
+
json_columns: list[Union[JSON, JSONB]] = []
|
278
|
+
email_columns: list[Email] = []
|
279
|
+
auto_update_columns: list[Column] = []
|
280
|
+
primary_key: Optional[Column] = None
|
281
|
+
m2m_relationships: list[M2M] = []
|
282
282
|
|
283
283
|
attribute_names = itertools.chain(
|
284
284
|
*[i.__dict__.keys() for i in reversed(cls.__mro__)]
|
@@ -314,15 +314,15 @@ class Table(metaclass=TableMetaclass):
|
|
314
314
|
if isinstance(column, Email):
|
315
315
|
email_columns.append(column)
|
316
316
|
|
317
|
-
if isinstance(column, Secret):
|
318
|
-
secret_columns.append(column)
|
319
|
-
|
320
317
|
if isinstance(column, ForeignKey):
|
321
318
|
foreign_key_columns.append(column)
|
322
319
|
|
323
320
|
if isinstance(column, (JSON, JSONB)):
|
324
321
|
json_columns.append(column)
|
325
322
|
|
323
|
+
if column._meta.secret:
|
324
|
+
secret_columns.append(column)
|
325
|
+
|
326
326
|
if column._meta.auto_update is not ...:
|
327
327
|
auto_update_columns.append(column)
|
328
328
|
|
@@ -372,7 +372,7 @@ class Table(metaclass=TableMetaclass):
|
|
372
372
|
|
373
373
|
def __init__(
|
374
374
|
self,
|
375
|
-
_data:
|
375
|
+
_data: Optional[dict[Column, Any]] = None,
|
376
376
|
_ignore_missing: bool = False,
|
377
377
|
_exists_in_db: bool = False,
|
378
378
|
**kwargs,
|
@@ -412,7 +412,7 @@ class Table(metaclass=TableMetaclass):
|
|
412
412
|
|
413
413
|
# This is used by get_or_create to indicate to the user whether it
|
414
414
|
# was an existing row or not.
|
415
|
-
self._was_created:
|
415
|
+
self._was_created: Optional[bool] = None
|
416
416
|
|
417
417
|
for column in self._meta.columns:
|
418
418
|
value = _data.get(column, ...)
|
@@ -423,7 +423,7 @@ class Table(metaclass=TableMetaclass):
|
|
423
423
|
|
424
424
|
if value is ...:
|
425
425
|
value = kwargs.pop(
|
426
|
-
|
426
|
+
cast(str, column._meta.db_column_name), ...
|
427
427
|
)
|
428
428
|
|
429
429
|
if value is ...:
|
@@ -456,7 +456,7 @@ class Table(metaclass=TableMetaclass):
|
|
456
456
|
|
457
457
|
@classmethod
|
458
458
|
def from_dict(
|
459
|
-
cls:
|
459
|
+
cls: type[TableInstance], data: dict[str, Any]
|
460
460
|
) -> TableInstance:
|
461
461
|
"""
|
462
462
|
Used when loading fixtures. It can be overriden by subclasses in case
|
@@ -468,8 +468,8 @@ class Table(metaclass=TableMetaclass):
|
|
468
468
|
###########################################################################
|
469
469
|
|
470
470
|
def save(
|
471
|
-
self, columns:
|
472
|
-
) ->
|
471
|
+
self, columns: Optional[Sequence[Union[Column, str]]] = None
|
472
|
+
) -> Union[Insert, Update]:
|
473
473
|
"""
|
474
474
|
A proxy to an insert or update query.
|
475
475
|
|
@@ -504,7 +504,7 @@ class Table(metaclass=TableMetaclass):
|
|
504
504
|
for i in columns
|
505
505
|
]
|
506
506
|
|
507
|
-
values:
|
507
|
+
values: dict[Column, Any] = {
|
508
508
|
i: getattr(self, i._meta.name, None) for i in column_instances
|
509
509
|
}
|
510
510
|
|
@@ -525,9 +525,7 @@ class Table(metaclass=TableMetaclass):
|
|
525
525
|
== getattr(self, self._meta.primary_key._meta.name)
|
526
526
|
)
|
527
527
|
|
528
|
-
def update_self(
|
529
|
-
self, values: t.Dict[t.Union[Column, str], t.Any]
|
530
|
-
) -> UpdateSelf:
|
528
|
+
def update_self(self, values: dict[Union[Column, str], Any]) -> UpdateSelf:
|
531
529
|
"""
|
532
530
|
This allows the user to update a single object - useful when the values
|
533
531
|
are derived from the database in some way.
|
@@ -581,7 +579,7 @@ class Table(metaclass=TableMetaclass):
|
|
581
579
|
|
582
580
|
def refresh(
|
583
581
|
self,
|
584
|
-
columns:
|
582
|
+
columns: Optional[Sequence[Column]] = None,
|
585
583
|
load_json: bool = False,
|
586
584
|
) -> Refresh:
|
587
585
|
"""
|
@@ -611,16 +609,16 @@ class Table(metaclass=TableMetaclass):
|
|
611
609
|
"""
|
612
610
|
return Refresh(instance=self, columns=columns, load_json=load_json)
|
613
611
|
|
614
|
-
@
|
612
|
+
@overload
|
615
613
|
def get_related(
|
616
614
|
self, foreign_key: ForeignKey[ReferencedTable]
|
617
615
|
) -> GetRelated[ReferencedTable]: ...
|
618
616
|
|
619
|
-
@
|
617
|
+
@overload
|
620
618
|
def get_related(self, foreign_key: str) -> GetRelated[Table]: ...
|
621
619
|
|
622
620
|
def get_related(
|
623
|
-
self, foreign_key:
|
621
|
+
self, foreign_key: Union[str, ForeignKey[ReferencedTable]]
|
624
622
|
) -> GetRelated[ReferencedTable]:
|
625
623
|
"""
|
626
624
|
Used to fetch a ``Table`` instance, for the target of a foreign key.
|
@@ -666,7 +664,7 @@ class Table(metaclass=TableMetaclass):
|
|
666
664
|
self,
|
667
665
|
*rows: Table,
|
668
666
|
m2m: M2M,
|
669
|
-
extra_column_values:
|
667
|
+
extra_column_values: dict[Union[Column, str], Any] = {},
|
670
668
|
) -> M2MAddRelated:
|
671
669
|
"""
|
672
670
|
Save the row if it doesn't already exist in the database, and insert
|
@@ -733,7 +731,7 @@ class Table(metaclass=TableMetaclass):
|
|
733
731
|
m2m=m2m,
|
734
732
|
)
|
735
733
|
|
736
|
-
def to_dict(self, *columns: Column) ->
|
734
|
+
def to_dict(self, *columns: Column) -> dict[str, Any]:
|
737
735
|
"""
|
738
736
|
A convenience method which returns a dictionary, mapping column names
|
739
737
|
to values for this table instance.
|
@@ -785,7 +783,7 @@ class Table(metaclass=TableMetaclass):
|
|
785
783
|
)
|
786
784
|
return output
|
787
785
|
|
788
|
-
def __setitem__(self, key: str, value:
|
786
|
+
def __setitem__(self, key: str, value: Any):
|
789
787
|
setattr(self, key, value)
|
790
788
|
|
791
789
|
def __getitem__(self, key: str):
|
@@ -858,8 +856,8 @@ class Table(metaclass=TableMetaclass):
|
|
858
856
|
|
859
857
|
@classmethod
|
860
858
|
def all_related(
|
861
|
-
cls, exclude:
|
862
|
-
) ->
|
859
|
+
cls, exclude: Optional[list[Union[str, ForeignKey]]] = None
|
860
|
+
) -> list[ForeignKey]:
|
863
861
|
"""
|
864
862
|
Used in conjunction with ``objects`` queries. Just as we can use
|
865
863
|
``all_related`` on a ``ForeignKey``, you can also use it for the table
|
@@ -908,8 +906,8 @@ class Table(metaclass=TableMetaclass):
|
|
908
906
|
|
909
907
|
@classmethod
|
910
908
|
def all_columns(
|
911
|
-
cls, exclude:
|
912
|
-
) ->
|
909
|
+
cls, exclude: Optional[Sequence[Union[str, Column]]] = None
|
910
|
+
) -> list[Column]:
|
913
911
|
"""
|
914
912
|
Used in conjunction with ``select`` queries. Just as we can use
|
915
913
|
``all_columns`` to retrieve all of the columns from a related table,
|
@@ -975,7 +973,7 @@ class Table(metaclass=TableMetaclass):
|
|
975
973
|
|
976
974
|
@classmethod
|
977
975
|
def insert(
|
978
|
-
cls:
|
976
|
+
cls: type[TableInstance], *rows: TableInstance
|
979
977
|
) -> Insert[TableInstance]:
|
980
978
|
"""
|
981
979
|
Insert rows into the database.
|
@@ -993,7 +991,7 @@ class Table(metaclass=TableMetaclass):
|
|
993
991
|
return query
|
994
992
|
|
995
993
|
@classmethod
|
996
|
-
def raw(cls, sql: str, *args:
|
994
|
+
def raw(cls, sql: str, *args: Any) -> Raw:
|
997
995
|
"""
|
998
996
|
Execute raw SQL queries on the underlying engine - use with caution!
|
999
997
|
|
@@ -1012,8 +1010,8 @@ class Table(metaclass=TableMetaclass):
|
|
1012
1010
|
|
1013
1011
|
@classmethod
|
1014
1012
|
def _process_column_args(
|
1015
|
-
cls, *columns:
|
1016
|
-
) ->
|
1013
|
+
cls, *columns: Union[Selectable, str]
|
1014
|
+
) -> Sequence[Selectable]:
|
1017
1015
|
"""
|
1018
1016
|
Users can specify some column arguments as either Column instances, or
|
1019
1017
|
as strings representing the column name, for convenience.
|
@@ -1030,7 +1028,7 @@ class Table(metaclass=TableMetaclass):
|
|
1030
1028
|
|
1031
1029
|
@classmethod
|
1032
1030
|
def select(
|
1033
|
-
cls, *columns:
|
1031
|
+
cls, *columns: Union[Selectable, str], exclude_secrets=False
|
1034
1032
|
) -> Select:
|
1035
1033
|
"""
|
1036
1034
|
Get data in the form of a list of dictionaries, with each dictionary
|
@@ -1109,8 +1107,8 @@ class Table(metaclass=TableMetaclass):
|
|
1109
1107
|
|
1110
1108
|
@classmethod
|
1111
1109
|
def objects(
|
1112
|
-
cls:
|
1113
|
-
*prefetch:
|
1110
|
+
cls: type[TableInstance],
|
1111
|
+
*prefetch: Union[ForeignKey, list[ForeignKey]],
|
1114
1112
|
) -> Objects[TableInstance]:
|
1115
1113
|
"""
|
1116
1114
|
Returns a list of table instances (each representing a row), which you
|
@@ -1151,8 +1149,8 @@ class Table(metaclass=TableMetaclass):
|
|
1151
1149
|
@classmethod
|
1152
1150
|
def count(
|
1153
1151
|
cls,
|
1154
|
-
column:
|
1155
|
-
distinct:
|
1152
|
+
column: Optional[Column] = None,
|
1153
|
+
distinct: Optional[Sequence[Column]] = None,
|
1156
1154
|
) -> Count:
|
1157
1155
|
"""
|
1158
1156
|
Count the number of matching rows::
|
@@ -1224,7 +1222,7 @@ class Table(metaclass=TableMetaclass):
|
|
1224
1222
|
@classmethod
|
1225
1223
|
def update(
|
1226
1224
|
cls,
|
1227
|
-
values:
|
1225
|
+
values: Optional[dict[Union[Column, str], Any]] = None,
|
1228
1226
|
force: bool = False,
|
1229
1227
|
use_auto_update: bool = True,
|
1230
1228
|
**kwargs,
|
@@ -1288,7 +1286,7 @@ class Table(metaclass=TableMetaclass):
|
|
1288
1286
|
@classmethod
|
1289
1287
|
def create_index(
|
1290
1288
|
cls,
|
1291
|
-
columns:
|
1289
|
+
columns: Union[list[Column], list[str]],
|
1292
1290
|
method: IndexMethod = IndexMethod.btree,
|
1293
1291
|
if_not_exists: bool = False,
|
1294
1292
|
) -> CreateIndex:
|
@@ -1311,7 +1309,7 @@ class Table(metaclass=TableMetaclass):
|
|
1311
1309
|
@classmethod
|
1312
1310
|
def drop_index(
|
1313
1311
|
cls,
|
1314
|
-
columns:
|
1312
|
+
columns: Union[list[Column], list[str]],
|
1315
1313
|
if_exists: bool = True,
|
1316
1314
|
) -> DropIndex:
|
1317
1315
|
"""
|
@@ -1328,7 +1326,7 @@ class Table(metaclass=TableMetaclass):
|
|
1328
1326
|
###########################################################################
|
1329
1327
|
|
1330
1328
|
@classmethod
|
1331
|
-
def _get_index_name(cls, column_names:
|
1329
|
+
def _get_index_name(cls, column_names: list[str]) -> str:
|
1332
1330
|
"""
|
1333
1331
|
Generates an index name from the table name and column names.
|
1334
1332
|
"""
|
@@ -1338,7 +1336,7 @@ class Table(metaclass=TableMetaclass):
|
|
1338
1336
|
|
1339
1337
|
@classmethod
|
1340
1338
|
def _table_str(
|
1341
|
-
cls, abbreviated=False, excluded_params:
|
1339
|
+
cls, abbreviated=False, excluded_params: Optional[list[str]] = None
|
1342
1340
|
):
|
1343
1341
|
"""
|
1344
1342
|
Returns a basic string representation of the table and its columns.
|
@@ -1357,7 +1355,7 @@ class Table(metaclass=TableMetaclass):
|
|
1357
1355
|
spacer = "\n "
|
1358
1356
|
columns = []
|
1359
1357
|
for col in cls._meta.columns:
|
1360
|
-
params:
|
1358
|
+
params: list[str] = []
|
1361
1359
|
for key, value in col._meta.params.items():
|
1362
1360
|
if key in excluded_params:
|
1363
1361
|
continue
|
@@ -1374,6 +1372,15 @@ class Table(metaclass=TableMetaclass):
|
|
1374
1372
|
columns.append(
|
1375
1373
|
f"{col._meta.name} = {col.__class__.__name__}({params_string})"
|
1376
1374
|
)
|
1375
|
+
|
1376
|
+
for m2m_relationship in cls._meta.m2m_relationships:
|
1377
|
+
joining_table_name = (
|
1378
|
+
m2m_relationship._meta.resolved_joining_table.__name__
|
1379
|
+
)
|
1380
|
+
columns.append(
|
1381
|
+
f"{m2m_relationship._meta.name} = M2M({joining_table_name})"
|
1382
|
+
)
|
1383
|
+
|
1377
1384
|
columns_string = spacer.join(columns)
|
1378
1385
|
tablename = repr(cls._meta.tablename)
|
1379
1386
|
|
@@ -1392,10 +1399,10 @@ class Table(metaclass=TableMetaclass):
|
|
1392
1399
|
|
1393
1400
|
def create_table_class(
|
1394
1401
|
class_name: str,
|
1395
|
-
bases:
|
1396
|
-
class_kwargs:
|
1397
|
-
class_members:
|
1398
|
-
) ->
|
1402
|
+
bases: tuple[type] = (Table,),
|
1403
|
+
class_kwargs: dict[str, Any] = {},
|
1404
|
+
class_members: dict[str, Any] = {},
|
1405
|
+
) -> type[Table]:
|
1399
1406
|
"""
|
1400
1407
|
Used to dynamically create ``Table``subclasses at runtime. Most users
|
1401
1408
|
will not require this. It's mostly used internally for Piccolo's
|
@@ -1411,8 +1418,8 @@ def create_table_class(
|
|
1411
1418
|
For example, `{'my_column': Varchar()}`.
|
1412
1419
|
|
1413
1420
|
"""
|
1414
|
-
return
|
1415
|
-
|
1421
|
+
return cast(
|
1422
|
+
type[Table],
|
1416
1423
|
types.new_class(
|
1417
1424
|
name=class_name,
|
1418
1425
|
bases=bases,
|
@@ -1427,7 +1434,7 @@ def create_table_class(
|
|
1427
1434
|
|
1428
1435
|
|
1429
1436
|
async def create_db_tables(
|
1430
|
-
*tables:
|
1437
|
+
*tables: type[Table], if_not_exists: bool = False
|
1431
1438
|
) -> None:
|
1432
1439
|
"""
|
1433
1440
|
Creates the database table for each ``Table`` class passed in. The tables
|
@@ -1458,7 +1465,7 @@ async def create_db_tables(
|
|
1458
1465
|
|
1459
1466
|
|
1460
1467
|
def create_db_tables_sync(
|
1461
|
-
*tables:
|
1468
|
+
*tables: type[Table], if_not_exists: bool = False
|
1462
1469
|
) -> None:
|
1463
1470
|
"""
|
1464
1471
|
A sync wrapper around :func:`create_db_tables`.
|
@@ -1466,7 +1473,7 @@ def create_db_tables_sync(
|
|
1466
1473
|
run_sync(create_db_tables(*tables, if_not_exists=if_not_exists))
|
1467
1474
|
|
1468
1475
|
|
1469
|
-
def create_tables(*tables:
|
1476
|
+
def create_tables(*tables: type[Table], if_not_exists: bool = False) -> None:
|
1470
1477
|
"""
|
1471
1478
|
This original implementation has been replaced, because it was synchronous,
|
1472
1479
|
and felt at odds with the rest of the Piccolo codebase which is async
|
@@ -1485,7 +1492,7 @@ def create_tables(*tables: t.Type[Table], if_not_exists: bool = False) -> None:
|
|
1485
1492
|
return create_db_tables_sync(*tables, if_not_exists=if_not_exists)
|
1486
1493
|
|
1487
1494
|
|
1488
|
-
async def drop_db_tables(*tables:
|
1495
|
+
async def drop_db_tables(*tables: type[Table]) -> None:
|
1489
1496
|
"""
|
1490
1497
|
Drops the database table for each ``Table`` class passed in. The tables
|
1491
1498
|
are dropped in the correct order, based on their foreign keys.
|
@@ -1518,14 +1525,14 @@ async def drop_db_tables(*tables: t.Type[Table]) -> None:
|
|
1518
1525
|
await atomic.run()
|
1519
1526
|
|
1520
1527
|
|
1521
|
-
def drop_db_tables_sync(*tables:
|
1528
|
+
def drop_db_tables_sync(*tables: type[Table]) -> None:
|
1522
1529
|
"""
|
1523
1530
|
A sync wrapper around :func:`drop_db_tables`.
|
1524
1531
|
"""
|
1525
1532
|
run_sync(drop_db_tables(*tables))
|
1526
1533
|
|
1527
1534
|
|
1528
|
-
def drop_tables(*tables:
|
1535
|
+
def drop_tables(*tables: type[Table]) -> None:
|
1529
1536
|
"""
|
1530
1537
|
This original implementation has been replaced, because it was synchronous,
|
1531
1538
|
and felt at odds with the rest of the Piccolo codebase which is async
|
@@ -1548,8 +1555,8 @@ def drop_tables(*tables: t.Type[Table]) -> None:
|
|
1548
1555
|
|
1549
1556
|
|
1550
1557
|
def sort_table_classes(
|
1551
|
-
table_classes:
|
1552
|
-
) ->
|
1558
|
+
table_classes: list[type[Table]],
|
1559
|
+
) -> list[type[Table]]:
|
1553
1560
|
"""
|
1554
1561
|
Sort the table classes based on their foreign keys, so they can be created
|
1555
1562
|
in the correct order.
|
@@ -1564,7 +1571,7 @@ def sort_table_classes(
|
|
1564
1571
|
sorter = TopologicalSorter(graph)
|
1565
1572
|
ordered_tablenames = tuple(sorter.static_order())
|
1566
1573
|
|
1567
|
-
output:
|
1574
|
+
output: list[type[Table]] = []
|
1568
1575
|
for tablename in ordered_tablenames:
|
1569
1576
|
table_class = table_class_dict.get(tablename)
|
1570
1577
|
if table_class is not None:
|
@@ -1574,10 +1581,10 @@ def sort_table_classes(
|
|
1574
1581
|
|
1575
1582
|
|
1576
1583
|
def _get_graph(
|
1577
|
-
table_classes:
|
1584
|
+
table_classes: list[type[Table]],
|
1578
1585
|
iterations: int = 0,
|
1579
1586
|
max_iterations: int = 5,
|
1580
|
-
) ->
|
1587
|
+
) -> dict[str, set[str]]:
|
1581
1588
|
"""
|
1582
1589
|
Analyses the tables based on their foreign keys, and returns a data
|
1583
1590
|
structure like:
|
@@ -1590,13 +1597,13 @@ def _get_graph(
|
|
1590
1597
|
to it via a foreign key.
|
1591
1598
|
|
1592
1599
|
"""
|
1593
|
-
output:
|
1600
|
+
output: dict[str, set[str]] = {}
|
1594
1601
|
|
1595
1602
|
if iterations >= max_iterations:
|
1596
1603
|
return output
|
1597
1604
|
|
1598
1605
|
for table_class in table_classes:
|
1599
|
-
dependents:
|
1606
|
+
dependents: set[str] = set()
|
1600
1607
|
for fk in table_class._meta.foreign_key_columns:
|
1601
1608
|
referenced_table = fk._foreign_key_meta.resolved_references
|
1602
1609
|
|
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)):
|