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
tests/table/test_alter.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
|
3
|
+
from typing import Any, Union
|
4
4
|
from unittest import TestCase
|
5
5
|
|
6
6
|
import pytest
|
@@ -26,7 +26,7 @@ from tests.example_apps.music.tables import Band, Manager
|
|
26
26
|
class TestRenameColumn(DBTestCase):
|
27
27
|
def _test_rename(
|
28
28
|
self,
|
29
|
-
existing_column:
|
29
|
+
existing_column: Union[Column, str],
|
30
30
|
new_column_name: str = "rating",
|
31
31
|
):
|
32
32
|
self.insert_row()
|
@@ -90,7 +90,7 @@ class TestDropColumn(DBTestCase):
|
|
90
90
|
SQLite has very limited support for ALTER statements.
|
91
91
|
"""
|
92
92
|
|
93
|
-
def _test_drop(self, column:
|
93
|
+
def _test_drop(self, column: Union[str, Column]):
|
94
94
|
self.insert_row()
|
95
95
|
|
96
96
|
Band.alter().drop_column(column).run_sync()
|
@@ -109,7 +109,7 @@ class TestDropColumn(DBTestCase):
|
|
109
109
|
|
110
110
|
class TestAddColumn(DBTestCase):
|
111
111
|
def _test_add_column(
|
112
|
-
self, column: Column, column_name: str, expected_value:
|
112
|
+
self, column: Column, column_name: str, expected_value: Any
|
113
113
|
):
|
114
114
|
self.insert_row()
|
115
115
|
Band.alter().add_column(column_name, column).run_sync()
|
tests/table/test_indexes.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import typing as t
|
2
1
|
from unittest import TestCase
|
3
2
|
|
4
3
|
from piccolo.columns.base import Column
|
@@ -52,7 +51,7 @@ class TestProblematicColumnName(TestCase):
|
|
52
51
|
Make sure we can add an index to a column with a problematic name
|
53
52
|
(which clashes with a SQL keyword).
|
54
53
|
"""
|
55
|
-
columns:
|
54
|
+
columns: list[Column] = [Concert.order]
|
56
55
|
Concert.create_index(columns=columns).run_sync()
|
57
56
|
index_name = Concert._get_index_name([i._meta.name for i in columns])
|
58
57
|
|
tests/table/test_refresh.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
from typing import cast
|
2
2
|
|
3
3
|
from piccolo.testing.test_case import TableTest
|
4
4
|
from tests.base import DBTestCase
|
@@ -293,6 +293,6 @@ class TestRefreshWithLoadJSON(TableTest):
|
|
293
293
|
self.recording_studio.refresh(load_json=True).run_sync()
|
294
294
|
|
295
295
|
self.assertDictEqual(
|
296
|
-
|
296
|
+
cast(dict, self.recording_studio.facilities),
|
297
297
|
{"electric piano": True},
|
298
298
|
)
|
tests/table/test_select.py
CHANGED
@@ -258,6 +258,64 @@ class TestSelect(DBTestCase):
|
|
258
258
|
|
259
259
|
self.assertEqual(response, [{"name": "Rustaceans"}])
|
260
260
|
|
261
|
+
def test_is_in(self):
|
262
|
+
self.insert_rows()
|
263
|
+
|
264
|
+
response = (
|
265
|
+
Band.select(Band.name)
|
266
|
+
.where(Band.manager._.name.is_in(["Guido"]))
|
267
|
+
.run_sync()
|
268
|
+
)
|
269
|
+
|
270
|
+
self.assertListEqual(response, [{"name": "Pythonistas"}])
|
271
|
+
|
272
|
+
def test_is_in_subquery(self):
|
273
|
+
self.insert_rows()
|
274
|
+
|
275
|
+
# This is a contrived example, just for testing.
|
276
|
+
response = (
|
277
|
+
Band.select(Band.name)
|
278
|
+
.where(
|
279
|
+
Band.manager.is_in(
|
280
|
+
Manager.select(Manager.id).where(Manager.name == "Guido")
|
281
|
+
)
|
282
|
+
)
|
283
|
+
.run_sync()
|
284
|
+
)
|
285
|
+
|
286
|
+
self.assertListEqual(response, [{"name": "Pythonistas"}])
|
287
|
+
|
288
|
+
def test_not_in(self):
|
289
|
+
self.insert_rows()
|
290
|
+
|
291
|
+
response = (
|
292
|
+
Band.select(Band.name)
|
293
|
+
.where(Band.manager._.name.not_in(["Guido"]))
|
294
|
+
.run_sync()
|
295
|
+
)
|
296
|
+
|
297
|
+
self.assertListEqual(
|
298
|
+
response, [{"name": "Rustaceans"}, {"name": "CSharps"}]
|
299
|
+
)
|
300
|
+
|
301
|
+
def test_not_in_subquery(self):
|
302
|
+
self.insert_rows()
|
303
|
+
|
304
|
+
# This is a contrived example, just for testing.
|
305
|
+
response = (
|
306
|
+
Band.select(Band.name)
|
307
|
+
.where(
|
308
|
+
Band.manager.not_in(
|
309
|
+
Manager.select(Manager.id).where(Manager.name == "Guido")
|
310
|
+
)
|
311
|
+
)
|
312
|
+
.run_sync()
|
313
|
+
)
|
314
|
+
|
315
|
+
self.assertListEqual(
|
316
|
+
response, [{"name": "Rustaceans"}, {"name": "CSharps"}]
|
317
|
+
)
|
318
|
+
|
261
319
|
def test_where_is_null(self):
|
262
320
|
self.insert_rows()
|
263
321
|
|
tests/table/test_update.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import dataclasses
|
2
2
|
import datetime
|
3
|
-
|
3
|
+
from typing import Any
|
4
4
|
from unittest import TestCase
|
5
5
|
|
6
6
|
import pytest
|
@@ -181,9 +181,9 @@ DATE_DELTA = datetime.timedelta(days=1)
|
|
181
181
|
class OperatorTestCase:
|
182
182
|
description: str
|
183
183
|
column: Column
|
184
|
-
initial:
|
184
|
+
initial: Any
|
185
185
|
querystring: QueryString
|
186
|
-
expected:
|
186
|
+
expected: Any
|
187
187
|
|
188
188
|
|
189
189
|
TEST_CASES = [
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
|
-
import typing as t
|
4
3
|
import unittest
|
5
4
|
|
6
5
|
from piccolo.columns import (
|
@@ -83,7 +82,7 @@ class TestModelBuilder(unittest.TestCase):
|
|
83
82
|
drop_db_tables_sync(*TABLES)
|
84
83
|
|
85
84
|
def test_async(self):
|
86
|
-
async def build_model(table_class:
|
85
|
+
async def build_model(table_class: type[Table]):
|
87
86
|
return await ModelBuilder.build(table_class)
|
88
87
|
|
89
88
|
for table_class in TABLES:
|
tests/utils/test_pydantic.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import decimal
|
2
|
-
|
2
|
+
from typing import Optional, cast
|
3
3
|
from unittest import TestCase
|
4
4
|
|
5
5
|
import pydantic
|
@@ -138,7 +138,7 @@ class TestArrayColumn(TestCase):
|
|
138
138
|
|
139
139
|
self.assertEqual(
|
140
140
|
pydantic_model.model_fields["members"].annotation,
|
141
|
-
|
141
|
+
list[list[pydantic.constr(max_length=255)]],
|
142
142
|
)
|
143
143
|
|
144
144
|
# Should not raise a validation error:
|
@@ -630,8 +630,8 @@ class TestNestedModel(TestCase):
|
|
630
630
|
|
631
631
|
#######################################################################
|
632
632
|
|
633
|
-
ManagerModel =
|
634
|
-
|
633
|
+
ManagerModel = cast(
|
634
|
+
type[pydantic.BaseModel],
|
635
635
|
BandModel.model_fields["manager"].annotation,
|
636
636
|
)
|
637
637
|
self.assertTrue(issubclass(ManagerModel, pydantic.BaseModel))
|
@@ -641,8 +641,8 @@ class TestNestedModel(TestCase):
|
|
641
641
|
|
642
642
|
#######################################################################
|
643
643
|
|
644
|
-
CountryModel =
|
645
|
-
|
644
|
+
CountryModel = cast(
|
645
|
+
type[pydantic.BaseModel],
|
646
646
|
ManagerModel.model_fields["country"].annotation,
|
647
647
|
)
|
648
648
|
self.assertTrue(issubclass(CountryModel, pydantic.BaseModel))
|
@@ -681,8 +681,8 @@ class TestNestedModel(TestCase):
|
|
681
681
|
|
682
682
|
BandModel = create_pydantic_model(table=Band, nested=(Band.manager,))
|
683
683
|
|
684
|
-
ManagerModel =
|
685
|
-
|
684
|
+
ManagerModel = cast(
|
685
|
+
type[pydantic.BaseModel],
|
686
686
|
BandModel.model_fields["manager"].annotation,
|
687
687
|
)
|
688
688
|
self.assertTrue(issubclass(ManagerModel, pydantic.BaseModel))
|
@@ -694,7 +694,7 @@ class TestNestedModel(TestCase):
|
|
694
694
|
AssistantManagerType = BandModel.model_fields[
|
695
695
|
"assistant_manager"
|
696
696
|
].annotation
|
697
|
-
self.assertIs(AssistantManagerType,
|
697
|
+
self.assertIs(AssistantManagerType, Optional[int])
|
698
698
|
|
699
699
|
#######################################################################
|
700
700
|
# Test two levels deep
|
@@ -703,8 +703,8 @@ class TestNestedModel(TestCase):
|
|
703
703
|
table=Band, nested=(Band.manager._.country,)
|
704
704
|
)
|
705
705
|
|
706
|
-
ManagerModel =
|
707
|
-
|
706
|
+
ManagerModel = cast(
|
707
|
+
type[pydantic.BaseModel],
|
708
708
|
BandModel.model_fields["manager"].annotation,
|
709
709
|
)
|
710
710
|
self.assertTrue(issubclass(ManagerModel, pydantic.BaseModel))
|
@@ -713,14 +713,14 @@ class TestNestedModel(TestCase):
|
|
713
713
|
)
|
714
714
|
self.assertEqual(ManagerModel.__qualname__, "Band.manager")
|
715
715
|
|
716
|
-
AssistantManagerType =
|
717
|
-
|
716
|
+
AssistantManagerType = cast(
|
717
|
+
type[pydantic.BaseModel],
|
718
718
|
BandModel.model_fields["assistant_manager"].annotation,
|
719
719
|
)
|
720
|
-
self.assertIs(AssistantManagerType,
|
720
|
+
self.assertIs(AssistantManagerType, Optional[int])
|
721
721
|
|
722
|
-
CountryModel =
|
723
|
-
|
722
|
+
CountryModel = cast(
|
723
|
+
type[pydantic.BaseModel],
|
724
724
|
ManagerModel.model_fields["country"].annotation,
|
725
725
|
)
|
726
726
|
self.assertTrue(issubclass(CountryModel, pydantic.BaseModel))
|
@@ -737,10 +737,10 @@ class TestNestedModel(TestCase):
|
|
737
737
|
)
|
738
738
|
|
739
739
|
VenueModel = ConcertModel.model_fields["venue"].annotation
|
740
|
-
self.assertIs(VenueModel,
|
740
|
+
self.assertIs(VenueModel, Optional[int])
|
741
741
|
|
742
|
-
BandModel =
|
743
|
-
|
742
|
+
BandModel = cast(
|
743
|
+
type[pydantic.BaseModel],
|
744
744
|
ConcertModel.model_fields["band_1"].annotation,
|
745
745
|
)
|
746
746
|
self.assertTrue(issubclass(BandModel, pydantic.BaseModel))
|
@@ -750,8 +750,8 @@ class TestNestedModel(TestCase):
|
|
750
750
|
)
|
751
751
|
self.assertEqual(BandModel.__qualname__, "Concert.band_1")
|
752
752
|
|
753
|
-
ManagerModel =
|
754
|
-
|
753
|
+
ManagerModel = cast(
|
754
|
+
type[pydantic.BaseModel],
|
755
755
|
BandModel.model_fields["manager"].annotation,
|
756
756
|
)
|
757
757
|
self.assertTrue(issubclass(ManagerModel, pydantic.BaseModel))
|
@@ -764,10 +764,10 @@ class TestNestedModel(TestCase):
|
|
764
764
|
AssistantManagerType = BandModel.model_fields[
|
765
765
|
"assistant_manager"
|
766
766
|
].annotation
|
767
|
-
self.assertIs(AssistantManagerType,
|
767
|
+
self.assertIs(AssistantManagerType, Optional[int])
|
768
768
|
|
769
769
|
CountryModel = ManagerModel.model_fields["country"].annotation
|
770
|
-
self.assertIs(CountryModel,
|
770
|
+
self.assertIs(CountryModel, Optional[int])
|
771
771
|
|
772
772
|
#######################################################################
|
773
773
|
# Test with `model_name` arg
|
@@ -778,8 +778,8 @@ class TestNestedModel(TestCase):
|
|
778
778
|
model_name="MyConcertModel",
|
779
779
|
)
|
780
780
|
|
781
|
-
BandModel =
|
782
|
-
|
781
|
+
BandModel = cast(
|
782
|
+
type[pydantic.BaseModel],
|
783
783
|
MyConcertModel.model_fields["band_1"].annotation,
|
784
784
|
)
|
785
785
|
self.assertEqual(BandModel.__qualname__, "MyConcertModel.band_1")
|
@@ -810,8 +810,8 @@ class TestNestedModel(TestCase):
|
|
810
810
|
table=Band, nested=True, include_default_columns=True
|
811
811
|
)
|
812
812
|
|
813
|
-
ManagerModel =
|
814
|
-
|
813
|
+
ManagerModel = cast(
|
814
|
+
type[pydantic.BaseModel],
|
815
815
|
BandModel.model_fields["manager"].annotation,
|
816
816
|
)
|
817
817
|
self.assertTrue(issubclass(ManagerModel, pydantic.BaseModel))
|
@@ -820,8 +820,8 @@ class TestNestedModel(TestCase):
|
|
820
820
|
["id", "name", "country"],
|
821
821
|
)
|
822
822
|
|
823
|
-
CountryModel =
|
824
|
-
|
823
|
+
CountryModel = cast(
|
824
|
+
type[pydantic.BaseModel],
|
825
825
|
ManagerModel.model_fields["country"].annotation,
|
826
826
|
)
|
827
827
|
self.assertTrue(issubclass(CountryModel, pydantic.BaseModel))
|
@@ -855,27 +855,27 @@ class TestRecursionDepth(TestCase):
|
|
855
855
|
table=Concert, nested=True, max_recursion_depth=2
|
856
856
|
)
|
857
857
|
|
858
|
-
VenueModel =
|
859
|
-
|
858
|
+
VenueModel = cast(
|
859
|
+
type[pydantic.BaseModel],
|
860
860
|
ConcertModel.model_fields["venue"].annotation,
|
861
861
|
)
|
862
862
|
self.assertTrue(issubclass(VenueModel, pydantic.BaseModel))
|
863
863
|
|
864
|
-
BandModel =
|
865
|
-
|
864
|
+
BandModel = cast(
|
865
|
+
type[pydantic.BaseModel],
|
866
866
|
ConcertModel.model_fields["band"].annotation,
|
867
867
|
)
|
868
868
|
self.assertTrue(issubclass(BandModel, pydantic.BaseModel))
|
869
869
|
|
870
|
-
ManagerModel =
|
871
|
-
|
870
|
+
ManagerModel = cast(
|
871
|
+
type[pydantic.BaseModel],
|
872
872
|
BandModel.model_fields["manager"].annotation,
|
873
873
|
)
|
874
874
|
self.assertTrue(issubclass(ManagerModel, pydantic.BaseModel))
|
875
875
|
|
876
876
|
# We should have hit the recursion depth:
|
877
877
|
CountryModel = ManagerModel.model_fields["country"].annotation
|
878
|
-
self.assertIs(CountryModel,
|
878
|
+
self.assertIs(CountryModel, Optional[int])
|
879
879
|
|
880
880
|
|
881
881
|
class TestDBColumnName(TestCase):
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import typing as t
|
2
1
|
from unittest import TestCase
|
3
2
|
|
4
3
|
from piccolo.columns import Varchar
|
@@ -22,7 +21,7 @@ class TestTableStorage(TestCase):
|
|
22
21
|
table_class.alter().drop_table(if_exists=True).run_sync()
|
23
22
|
|
24
23
|
def _compare_table_columns(
|
25
|
-
self, table_1:
|
24
|
+
self, table_1: type[Table], table_2: type[Table]
|
26
25
|
):
|
27
26
|
"""
|
28
27
|
Make sure that for each column in table_1, there is a corresponding
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|