piccolo 1.10.0__py3-none-any.whl → 1.12.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.
Files changed (42) hide show
  1. piccolo/__init__.py +1 -1
  2. piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja +35 -30
  3. piccolo/apps/asgi/commands/templates/app/_starlette_app.py.jinja +29 -21
  4. piccolo/apps/migrations/auto/migration_manager.py +1 -0
  5. piccolo/apps/migrations/commands/forwards.py +8 -7
  6. piccolo/apps/playground/commands/run.py +11 -0
  7. piccolo/columns/column_types.py +83 -35
  8. piccolo/query/functions/__init__.py +11 -1
  9. piccolo/query/functions/datetime.py +260 -0
  10. piccolo/query/functions/string.py +45 -0
  11. {piccolo-1.10.0.dist-info → piccolo-1.12.0.dist-info}/METADATA +15 -15
  12. {piccolo-1.10.0.dist-info → piccolo-1.12.0.dist-info}/RECORD +42 -40
  13. {piccolo-1.10.0.dist-info → piccolo-1.12.0.dist-info}/WHEEL +1 -1
  14. tests/apps/migrations/commands/test_forwards_backwards.py +32 -1
  15. tests/columns/test_array.py +3 -7
  16. tests/columns/test_bigint.py +3 -9
  17. tests/columns/test_boolean.py +3 -7
  18. tests/columns/test_bytea.py +5 -14
  19. tests/columns/test_choices.py +5 -14
  20. tests/columns/test_date.py +5 -13
  21. tests/columns/test_double_precision.py +3 -8
  22. tests/columns/test_interval.py +5 -13
  23. tests/columns/test_json.py +9 -26
  24. tests/columns/test_jsonb.py +3 -11
  25. tests/columns/test_numeric.py +3 -7
  26. tests/columns/test_primary_key.py +11 -33
  27. tests/columns/test_readable.py +5 -7
  28. tests/columns/test_real.py +3 -8
  29. tests/columns/test_reserved_column_names.py +3 -8
  30. tests/columns/test_smallint.py +3 -8
  31. tests/columns/test_time.py +5 -14
  32. tests/columns/test_timestamp.py +5 -13
  33. tests/columns/test_timestamptz.py +5 -13
  34. tests/columns/test_uuid.py +3 -7
  35. tests/columns/test_varchar.py +3 -9
  36. tests/query/functions/base.py +2 -15
  37. tests/query/functions/test_datetime.py +112 -0
  38. tests/query/functions/test_math.py +2 -3
  39. tests/query/functions/test_string.py +34 -2
  40. {piccolo-1.10.0.dist-info → piccolo-1.12.0.dist-info}/LICENSE +0 -0
  41. {piccolo-1.10.0.dist-info → piccolo-1.12.0.dist-info}/entry_points.txt +0 -0
  42. {piccolo-1.10.0.dist-info → piccolo-1.12.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,7 @@
1
- from unittest import TestCase
2
-
3
1
  from piccolo.columns.column_types import Varchar
4
2
  from piccolo.table import Table
5
3
 
6
- from ..base import engines_only
4
+ from ..base import TableTest, engines_only
7
5
 
8
6
 
9
7
  class MyTable(Table):
@@ -11,7 +9,7 @@ class MyTable(Table):
11
9
 
12
10
 
13
11
  @engines_only("postgres", "cockroach")
14
- class TestVarchar(TestCase):
12
+ class TestVarchar(TableTest):
15
13
  """
16
14
  SQLite doesn't enforce any constraints on max character length.
17
15
 
@@ -20,11 +18,7 @@ class TestVarchar(TestCase):
20
18
  Might consider enforcing this at the ORM level instead in the future.
21
19
  """
22
20
 
23
- def setUp(self):
24
- MyTable.create_table().run_sync()
25
-
26
- def tearDown(self):
27
- MyTable.alter().drop_table().run_sync()
21
+ tables = [MyTable]
28
22
 
29
23
  def test_length(self):
30
24
  row = MyTable(name="bob")
@@ -1,21 +1,8 @@
1
- import typing as t
2
- from unittest import TestCase
3
-
4
- from piccolo.table import Table, create_db_tables_sync, drop_db_tables_sync
1
+ from tests.base import TableTest
5
2
  from tests.example_apps.music.tables import Band, Manager
6
3
 
7
4
 
8
- class FunctionTest(TestCase):
9
- tables: t.List[t.Type[Table]]
10
-
11
- def setUp(self) -> None:
12
- create_db_tables_sync(*self.tables)
13
-
14
- def tearDown(self) -> None:
15
- drop_db_tables_sync(*self.tables)
16
-
17
-
18
- class BandTest(FunctionTest):
5
+ class BandTest(TableTest):
19
6
  tables = [Band, Manager]
20
7
 
21
8
  def setUp(self) -> None:
@@ -0,0 +1,112 @@
1
+ import datetime
2
+
3
+ from piccolo.columns import Timestamp
4
+ from piccolo.query.functions.datetime import (
5
+ Day,
6
+ Extract,
7
+ Hour,
8
+ Minute,
9
+ Month,
10
+ Second,
11
+ Strftime,
12
+ Year,
13
+ )
14
+ from piccolo.table import Table
15
+ from tests.base import TableTest, engines_only, sqlite_only
16
+
17
+
18
+ class Concert(Table):
19
+ starts = Timestamp()
20
+
21
+
22
+ class DatetimeTest(TableTest):
23
+ tables = [Concert]
24
+
25
+ def setUp(self) -> None:
26
+ super().setUp()
27
+ self.concert = Concert(
28
+ {
29
+ Concert.starts: datetime.datetime(
30
+ year=2024, month=6, day=14, hour=23, minute=46, second=10
31
+ )
32
+ }
33
+ )
34
+ self.concert.save().run_sync()
35
+
36
+
37
+ @engines_only("postgres", "cockroach")
38
+ class TestExtract(DatetimeTest):
39
+ def test_extract(self):
40
+ self.assertEqual(
41
+ Concert.select(
42
+ Extract(Concert.starts, "year", alias="starts_year")
43
+ ).run_sync(),
44
+ [{"starts_year": self.concert.starts.year}],
45
+ )
46
+
47
+ def test_invalid_format(self):
48
+ with self.assertRaises(ValueError):
49
+ Extract(
50
+ Concert.starts,
51
+ "abc123", # type: ignore
52
+ alias="starts_year",
53
+ )
54
+
55
+
56
+ @sqlite_only
57
+ class TestStrftime(DatetimeTest):
58
+ def test_strftime(self):
59
+ self.assertEqual(
60
+ Concert.select(
61
+ Strftime(Concert.starts, "%Y", alias="starts_year")
62
+ ).run_sync(),
63
+ [{"starts_year": str(self.concert.starts.year)}],
64
+ )
65
+
66
+
67
+ class TestDatabaseAgnostic(DatetimeTest):
68
+ def test_year(self):
69
+ self.assertEqual(
70
+ Concert.select(
71
+ Year(Concert.starts, alias="starts_year")
72
+ ).run_sync(),
73
+ [{"starts_year": self.concert.starts.year}],
74
+ )
75
+
76
+ def test_month(self):
77
+ self.assertEqual(
78
+ Concert.select(
79
+ Month(Concert.starts, alias="starts_month")
80
+ ).run_sync(),
81
+ [{"starts_month": self.concert.starts.month}],
82
+ )
83
+
84
+ def test_day(self):
85
+ self.assertEqual(
86
+ Concert.select(Day(Concert.starts, alias="starts_day")).run_sync(),
87
+ [{"starts_day": self.concert.starts.day}],
88
+ )
89
+
90
+ def test_hour(self):
91
+ self.assertEqual(
92
+ Concert.select(
93
+ Hour(Concert.starts, alias="starts_hour")
94
+ ).run_sync(),
95
+ [{"starts_hour": self.concert.starts.hour}],
96
+ )
97
+
98
+ def test_minute(self):
99
+ self.assertEqual(
100
+ Concert.select(
101
+ Minute(Concert.starts, alias="starts_minute")
102
+ ).run_sync(),
103
+ [{"starts_minute": self.concert.starts.minute}],
104
+ )
105
+
106
+ def test_second(self):
107
+ self.assertEqual(
108
+ Concert.select(
109
+ Second(Concert.starts, alias="starts_second")
110
+ ).run_sync(),
111
+ [{"starts_second": self.concert.starts.second}],
112
+ )
@@ -3,15 +3,14 @@ import decimal
3
3
  from piccolo.columns import Numeric
4
4
  from piccolo.query.functions.math import Abs, Ceil, Floor, Round
5
5
  from piccolo.table import Table
6
-
7
- from .base import FunctionTest
6
+ from tests.base import TableTest
8
7
 
9
8
 
10
9
  class Ticket(Table):
11
10
  price = Numeric(digits=(5, 2))
12
11
 
13
12
 
14
- class TestMath(FunctionTest):
13
+ class TestMath(TableTest):
15
14
 
16
15
  tables = [Ticket]
17
16
 
@@ -1,10 +1,13 @@
1
- from piccolo.query.functions.string import Upper
1
+ import pytest
2
+
3
+ from piccolo.query.functions.string import Concat, Upper
4
+ from tests.base import engine_version_lt, is_running_sqlite
2
5
  from tests.example_apps.music.tables import Band
3
6
 
4
7
  from .base import BandTest
5
8
 
6
9
 
7
- class TestUpperFunction(BandTest):
10
+ class TestUpper(BandTest):
8
11
 
9
12
  def test_column(self):
10
13
  """
@@ -23,3 +26,32 @@ class TestUpperFunction(BandTest):
23
26
  """
24
27
  response = Band.select(Upper(Band.manager._.name)).run_sync()
25
28
  self.assertListEqual(response, [{"upper": "GUIDO"}])
29
+
30
+
31
+ @pytest.mark.skipif(
32
+ is_running_sqlite() and engine_version_lt(3.44),
33
+ reason="SQLite version not supported",
34
+ )
35
+ class TestConcat(BandTest):
36
+
37
+ def test_column_and_string(self):
38
+ response = Band.select(
39
+ Concat(Band.name, "!!!", alias="name")
40
+ ).run_sync()
41
+ self.assertListEqual(response, [{"name": "Pythonistas!!!"}])
42
+
43
+ def test_column_and_column(self):
44
+ response = Band.select(
45
+ Concat(Band.name, Band.popularity, alias="name")
46
+ ).run_sync()
47
+ self.assertListEqual(response, [{"name": "Pythonistas1000"}])
48
+
49
+ def test_join(self):
50
+ response = Band.select(
51
+ Concat(Band.name, "-", Band.manager._.name, alias="name")
52
+ ).run_sync()
53
+ self.assertListEqual(response, [{"name": "Pythonistas-Guido"}])
54
+
55
+ def test_min_args(self):
56
+ with self.assertRaises(ValueError):
57
+ Concat()