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/apps/user/tables.py
CHANGED
@@ -8,7 +8,7 @@ import datetime
|
|
8
8
|
import hashlib
|
9
9
|
import logging
|
10
10
|
import secrets
|
11
|
-
|
11
|
+
from typing import Any, Optional, Union
|
12
12
|
|
13
13
|
from piccolo.columns import Boolean, Secret, Timestamp, Varchar
|
14
14
|
from piccolo.columns.column_types import Serial
|
@@ -109,14 +109,14 @@ class BaseUser(Table, tablename="piccolo_user"):
|
|
109
109
|
###########################################################################
|
110
110
|
|
111
111
|
@classmethod
|
112
|
-
def update_password_sync(cls, user:
|
112
|
+
def update_password_sync(cls, user: Union[str, int], password: str):
|
113
113
|
"""
|
114
114
|
A sync equivalent of :meth:`update_password`.
|
115
115
|
"""
|
116
116
|
return run_sync(cls.update_password(user, password))
|
117
117
|
|
118
118
|
@classmethod
|
119
|
-
async def update_password(cls, user:
|
119
|
+
async def update_password(cls, user: Union[str, int], password: str):
|
120
120
|
"""
|
121
121
|
The password is the raw password string e.g. ``'password123'``.
|
122
122
|
The user can be a user ID, or a username.
|
@@ -139,7 +139,7 @@ class BaseUser(Table, tablename="piccolo_user"):
|
|
139
139
|
|
140
140
|
@classmethod
|
141
141
|
def hash_password(
|
142
|
-
cls, password: str, salt: str = "", iterations:
|
142
|
+
cls, password: str, salt: str = "", iterations: Optional[int] = None
|
143
143
|
) -> str:
|
144
144
|
"""
|
145
145
|
Hashes the password, ready for storage, and for comparing during
|
@@ -167,7 +167,7 @@ class BaseUser(Table, tablename="piccolo_user"):
|
|
167
167
|
).hex()
|
168
168
|
return f"pbkdf2_sha256${iterations}${salt}${hashed}"
|
169
169
|
|
170
|
-
def __setattr__(self, name: str, value:
|
170
|
+
def __setattr__(self, name: str, value: Any):
|
171
171
|
"""
|
172
172
|
Make sure that if the password is set, it's stored in a hashed form.
|
173
173
|
"""
|
@@ -177,7 +177,7 @@ class BaseUser(Table, tablename="piccolo_user"):
|
|
177
177
|
super().__setattr__(name, value)
|
178
178
|
|
179
179
|
@classmethod
|
180
|
-
def split_stored_password(cls, password: str) ->
|
180
|
+
def split_stored_password(cls, password: str) -> list[str]:
|
181
181
|
elements = password.split("$")
|
182
182
|
if len(elements) != 4:
|
183
183
|
raise ValueError("Unable to split hashed password")
|
@@ -186,14 +186,14 @@ class BaseUser(Table, tablename="piccolo_user"):
|
|
186
186
|
###########################################################################
|
187
187
|
|
188
188
|
@classmethod
|
189
|
-
def login_sync(cls, username: str, password: str) ->
|
189
|
+
def login_sync(cls, username: str, password: str) -> Optional[int]:
|
190
190
|
"""
|
191
191
|
A sync equivalent of :meth:`login`.
|
192
192
|
"""
|
193
193
|
return run_sync(cls.login(username, password))
|
194
194
|
|
195
195
|
@classmethod
|
196
|
-
async def login(cls, username: str, password: str) ->
|
196
|
+
async def login(cls, username: str, password: str) -> Optional[int]:
|
197
197
|
"""
|
198
198
|
Make sure the user exists and the password is valid. If so, the
|
199
199
|
``last_login`` value is updated in the database.
|
piccolo/columns/base.py
CHANGED
@@ -4,10 +4,20 @@ import copy
|
|
4
4
|
import datetime
|
5
5
|
import decimal
|
6
6
|
import inspect
|
7
|
-
import typing as t
|
8
7
|
import uuid
|
8
|
+
from collections.abc import Iterable
|
9
9
|
from dataclasses import dataclass, field, fields
|
10
10
|
from enum import Enum
|
11
|
+
from typing import (
|
12
|
+
TYPE_CHECKING,
|
13
|
+
Any,
|
14
|
+
Generic,
|
15
|
+
Optional,
|
16
|
+
TypedDict,
|
17
|
+
TypeVar,
|
18
|
+
Union,
|
19
|
+
cast,
|
20
|
+
)
|
11
21
|
|
12
22
|
from piccolo.columns.choices import Choice
|
13
23
|
from piccolo.columns.combination import Where
|
@@ -34,8 +44,9 @@ from piccolo.columns.reference import LazyTableReference
|
|
34
44
|
from piccolo.querystring import QueryString, Selectable
|
35
45
|
from piccolo.utils.warnings import colored_warning
|
36
46
|
|
37
|
-
if
|
47
|
+
if TYPE_CHECKING: # pragma: no cover
|
38
48
|
from piccolo.columns.column_types import ForeignKey
|
49
|
+
from piccolo.query.methods.select import Select
|
39
50
|
from piccolo.table import Table
|
40
51
|
|
41
52
|
|
@@ -77,19 +88,19 @@ class OnUpdate(str, Enum):
|
|
77
88
|
return self.__str__()
|
78
89
|
|
79
90
|
|
80
|
-
ReferencedTable =
|
91
|
+
ReferencedTable = TypeVar("ReferencedTable", bound="Table")
|
81
92
|
|
82
93
|
|
83
94
|
@dataclass
|
84
|
-
class ForeignKeyMeta(
|
85
|
-
references:
|
95
|
+
class ForeignKeyMeta(Generic[ReferencedTable]):
|
96
|
+
references: Union[type[ReferencedTable], LazyTableReference]
|
86
97
|
on_delete: OnDelete
|
87
98
|
on_update: OnUpdate
|
88
|
-
target_column:
|
89
|
-
proxy_columns:
|
99
|
+
target_column: Union[Column, str, None]
|
100
|
+
proxy_columns: list[Column] = field(default_factory=list)
|
90
101
|
|
91
102
|
@property
|
92
|
-
def resolved_references(self) ->
|
103
|
+
def resolved_references(self) -> type[Table]:
|
93
104
|
"""
|
94
105
|
Evaluates the ``references`` attribute if it's a ``LazyTableReference``,
|
95
106
|
raising a ``ValueError`` if it fails, otherwise returns a ``Table``
|
@@ -154,18 +165,18 @@ class ColumnMeta:
|
|
154
165
|
index: bool = False
|
155
166
|
index_method: IndexMethod = IndexMethod.btree
|
156
167
|
required: bool = False
|
157
|
-
help_text:
|
158
|
-
choices:
|
168
|
+
help_text: Optional[str] = None
|
169
|
+
choices: Optional[type[Enum]] = None
|
159
170
|
secret: bool = False
|
160
|
-
auto_update:
|
171
|
+
auto_update: Any = ...
|
161
172
|
|
162
173
|
# Used for representing the table in migrations and the playground.
|
163
|
-
params:
|
174
|
+
params: dict[str, Any] = field(default_factory=dict)
|
164
175
|
|
165
176
|
###########################################################################
|
166
177
|
|
167
178
|
# Lets you to map a column to a database column with a different name.
|
168
|
-
_db_column_name:
|
179
|
+
_db_column_name: Optional[str] = None
|
169
180
|
|
170
181
|
@property
|
171
182
|
def db_column_name(self) -> str:
|
@@ -178,8 +189,8 @@ class ColumnMeta:
|
|
178
189
|
###########################################################################
|
179
190
|
|
180
191
|
# Set by the Table Metaclass:
|
181
|
-
_name:
|
182
|
-
_table:
|
192
|
+
_name: Optional[str] = None
|
193
|
+
_table: Optional[type[Table]] = None
|
183
194
|
|
184
195
|
@property
|
185
196
|
def name(self) -> str:
|
@@ -194,7 +205,7 @@ class ColumnMeta:
|
|
194
205
|
self._name = value
|
195
206
|
|
196
207
|
@property
|
197
|
-
def table(self) ->
|
208
|
+
def table(self) -> type[Table]:
|
198
209
|
if not self._table:
|
199
210
|
raise ValueError(
|
200
211
|
"`_table` isn't defined - the Table Metaclass should set it."
|
@@ -202,13 +213,13 @@ class ColumnMeta:
|
|
202
213
|
return self._table
|
203
214
|
|
204
215
|
@table.setter
|
205
|
-
def table(self, value:
|
216
|
+
def table(self, value: type[Table]):
|
206
217
|
self._table = value
|
207
218
|
|
208
219
|
###########################################################################
|
209
220
|
|
210
221
|
# Used by Foreign Keys:
|
211
|
-
call_chain:
|
222
|
+
call_chain: list["ForeignKey"] = field(default_factory=list)
|
212
223
|
|
213
224
|
###########################################################################
|
214
225
|
|
@@ -220,7 +231,7 @@ class ColumnMeta:
|
|
220
231
|
else:
|
221
232
|
raise ValueError("The table has no engine defined.")
|
222
233
|
|
223
|
-
def get_choices_dict(self) ->
|
234
|
+
def get_choices_dict(self) -> Optional[dict[str, Any]]:
|
224
235
|
"""
|
225
236
|
Return the choices Enum as a dict. It maps the attribute name to a
|
226
237
|
dict containing the display name, and value.
|
@@ -251,8 +262,7 @@ class ColumnMeta:
|
|
251
262
|
if self.call_chain:
|
252
263
|
column_name = (
|
253
264
|
".".join(
|
254
|
-
|
255
|
-
for i in self.call_chain
|
265
|
+
cast(str, i._meta.db_column_name) for i in self.call_chain
|
256
266
|
)
|
257
267
|
+ f".{column_name}"
|
258
268
|
)
|
@@ -350,18 +360,18 @@ class ColumnMeta:
|
|
350
360
|
return self.copy()
|
351
361
|
|
352
362
|
|
353
|
-
class ColumnKwargs(
|
363
|
+
class ColumnKwargs(TypedDict, total=False):
|
354
364
|
null: bool
|
355
365
|
primary_key: bool
|
356
366
|
unique: bool
|
357
367
|
index: bool
|
358
368
|
index_method: IndexMethod
|
359
369
|
required: bool
|
360
|
-
help_text:
|
361
|
-
choices:
|
362
|
-
db_column_name:
|
370
|
+
help_text: Optional[str]
|
371
|
+
choices: Optional[type[Enum]]
|
372
|
+
db_column_name: Optional[str]
|
363
373
|
secret: bool
|
364
|
-
auto_update:
|
374
|
+
auto_update: Any
|
365
375
|
|
366
376
|
|
367
377
|
class Column(Selectable):
|
@@ -463,8 +473,8 @@ class Column(Selectable):
|
|
463
473
|
|
464
474
|
"""
|
465
475
|
|
466
|
-
value_type:
|
467
|
-
default:
|
476
|
+
value_type: type = int
|
477
|
+
default: Any
|
468
478
|
|
469
479
|
def __init__(
|
470
480
|
self,
|
@@ -474,11 +484,11 @@ class Column(Selectable):
|
|
474
484
|
index: bool = False,
|
475
485
|
index_method: IndexMethod = IndexMethod.btree,
|
476
486
|
required: bool = False,
|
477
|
-
help_text:
|
478
|
-
choices:
|
479
|
-
db_column_name:
|
487
|
+
help_text: Optional[str] = None,
|
488
|
+
choices: Optional[type[Enum]] = None,
|
489
|
+
db_column_name: Optional[str] = None,
|
480
490
|
secret: bool = False,
|
481
|
-
auto_update:
|
491
|
+
auto_update: Any = ...,
|
482
492
|
**kwargs,
|
483
493
|
) -> None:
|
484
494
|
# This is for backwards compatibility - originally the `primary_key`
|
@@ -522,12 +532,12 @@ class Column(Selectable):
|
|
522
532
|
auto_update=auto_update,
|
523
533
|
)
|
524
534
|
|
525
|
-
self._alias:
|
535
|
+
self._alias: Optional[str] = None
|
526
536
|
|
527
537
|
def _validate_default(
|
528
538
|
self,
|
529
|
-
default:
|
530
|
-
allowed_types:
|
539
|
+
default: Any,
|
540
|
+
allowed_types: Iterable[Union[None, type[Any]]],
|
531
541
|
allow_recursion: bool = True,
|
532
542
|
) -> bool:
|
533
543
|
"""
|
@@ -564,7 +574,7 @@ class Column(Selectable):
|
|
564
574
|
)
|
565
575
|
|
566
576
|
def _validate_choices(
|
567
|
-
self, choices:
|
577
|
+
self, choices: type[Enum], allowed_type: type[Any]
|
568
578
|
) -> bool:
|
569
579
|
"""
|
570
580
|
Make sure the choices value has values of the allowed_type.
|
@@ -590,18 +600,40 @@ class Column(Selectable):
|
|
590
600
|
|
591
601
|
return True
|
592
602
|
|
593
|
-
def is_in(self, values:
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
)
|
603
|
+
def is_in(self, values: Union[Select, QueryString, list[Any]]) -> Where:
|
604
|
+
from piccolo.query.methods.select import Select
|
605
|
+
|
606
|
+
if isinstance(values, list):
|
607
|
+
if len(values) == 0:
|
608
|
+
raise ValueError(
|
609
|
+
"The `values` list argument must contain at least one "
|
610
|
+
"value."
|
611
|
+
)
|
612
|
+
elif isinstance(values, Select):
|
613
|
+
if len(values.columns_delegate.selected_columns) != 1:
|
614
|
+
raise ValueError(
|
615
|
+
"A sub select must only return a single column."
|
616
|
+
)
|
617
|
+
values = values.querystrings[0]
|
618
|
+
|
598
619
|
return Where(column=self, values=values, operator=In)
|
599
620
|
|
600
|
-
def not_in(self, values:
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
)
|
621
|
+
def not_in(self, values: Union[Select, QueryString, list[Any]]) -> Where:
|
622
|
+
from piccolo.query.methods.select import Select
|
623
|
+
|
624
|
+
if isinstance(values, list):
|
625
|
+
if len(values) == 0:
|
626
|
+
raise ValueError(
|
627
|
+
"The `values` list argument must contain at least one "
|
628
|
+
"value."
|
629
|
+
)
|
630
|
+
elif isinstance(values, Select):
|
631
|
+
if len(values.columns_delegate.selected_columns) != 1:
|
632
|
+
raise ValueError(
|
633
|
+
"A sub select must only return a single column."
|
634
|
+
)
|
635
|
+
values = values.querystrings[0]
|
636
|
+
|
605
637
|
return Where(column=self, values=values, operator=NotIn)
|
606
638
|
|
607
639
|
def like(self, value: str) -> Where:
|
@@ -628,7 +660,7 @@ class Column(Selectable):
|
|
628
660
|
|
629
661
|
"""
|
630
662
|
if self._meta.engine_type in ("postgres", "cockroach"):
|
631
|
-
operator:
|
663
|
+
operator: type[ComparisonOperator] = ILike
|
632
664
|
else:
|
633
665
|
colored_warning(
|
634
666
|
"SQLite doesn't support ILIKE, falling back to LIKE."
|
@@ -799,7 +831,7 @@ class Column(Selectable):
|
|
799
831
|
virtual_foreign_key.set_proxy_columns()
|
800
832
|
return virtual_foreign_key
|
801
833
|
|
802
|
-
def get_default_value(self) ->
|
834
|
+
def get_default_value(self) -> Any:
|
803
835
|
"""
|
804
836
|
If the column has a default attribute, return it. If it's callable,
|
805
837
|
return the response instead.
|
@@ -845,7 +877,7 @@ class Column(Selectable):
|
|
845
877
|
|
846
878
|
def get_sql_value(
|
847
879
|
self,
|
848
|
-
value:
|
880
|
+
value: Any,
|
849
881
|
delimiter: str = "'",
|
850
882
|
) -> str:
|
851
883
|
"""
|
@@ -946,8 +978,8 @@ class Column(Selectable):
|
|
946
978
|
if not self._meta.null:
|
947
979
|
query += " NOT NULL"
|
948
980
|
|
949
|
-
foreign_key_meta =
|
950
|
-
|
981
|
+
foreign_key_meta = cast(
|
982
|
+
Optional[ForeignKeyMeta],
|
951
983
|
getattr(self, "_foreign_key_meta", None),
|
952
984
|
)
|
953
985
|
if foreign_key_meta:
|
@@ -1004,4 +1036,4 @@ class Column(Selectable):
|
|
1004
1036
|
)
|
1005
1037
|
|
1006
1038
|
|
1007
|
-
Self =
|
1039
|
+
Self = TypeVar("Self", bound=Column)
|
piccolo/columns/choices.py
CHANGED