piccolo 1.28.0__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/asgi/commands/new.py +1 -1
- 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/playground/commands/run.py +43 -0
- piccolo/columns/column_types.py +2 -2
- piccolo/table.py +14 -6
- piccolo/testing/model_builder.py +4 -2
- piccolo/testing/random_builder.py +12 -0
- {piccolo-1.28.0.dist-info → piccolo-1.29.0.dist-info}/METADATA +1 -1
- {piccolo-1.28.0.dist-info → piccolo-1.29.0.dist-info}/RECORD +26 -26
- tests/columns/m2m/base.py +18 -47
- tests/table/test_metaclass.py +7 -3
- tests/table/test_str.py +30 -22
- tests/table/test_update.py +15 -0
- tests/testing/test_random_builder.py +5 -0
- tests/utils/test_pydantic.py +116 -98
- {piccolo-1.28.0.dist-info → piccolo-1.29.0.dist-info}/WHEEL +0 -0
- {piccolo-1.28.0.dist-info → piccolo-1.29.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.28.0.dist-info → piccolo-1.29.0.dist-info}/licenses/LICENSE +0 -0
- {piccolo-1.28.0.dist-info → piccolo-1.29.0.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import decimal
|
1
2
|
import unittest
|
2
3
|
from enum import Enum
|
3
4
|
|
@@ -35,6 +36,10 @@ class TestRandomBuilder(unittest.TestCase):
|
|
35
36
|
random_float = RandomBuilder.next_float(maximum=1000)
|
36
37
|
self.assertLessEqual(random_float, 1000)
|
37
38
|
|
39
|
+
def test_next_decimal(self):
|
40
|
+
random_decimal = RandomBuilder.next_decimal(precision=4, scale=2)
|
41
|
+
self.assertLessEqual(random_decimal, decimal.Decimal("99.99"))
|
42
|
+
|
38
43
|
def test_next_int(self):
|
39
44
|
random_int = RandomBuilder.next_int()
|
40
45
|
self.assertLessEqual(random_int, 2147483647)
|
tests/utils/test_pydantic.py
CHANGED
@@ -29,10 +29,10 @@ from piccolo.utils.pydantic import create_pydantic_model
|
|
29
29
|
|
30
30
|
class TestVarcharColumn(TestCase):
|
31
31
|
def test_varchar_length(self):
|
32
|
-
class
|
32
|
+
class Manager(Table):
|
33
33
|
name = Varchar(length=10)
|
34
34
|
|
35
|
-
pydantic_model = create_pydantic_model(table=
|
35
|
+
pydantic_model = create_pydantic_model(table=Manager)
|
36
36
|
|
37
37
|
with self.assertRaises(ValidationError):
|
38
38
|
pydantic_model(name="This is a really long name")
|
@@ -42,10 +42,10 @@ class TestVarcharColumn(TestCase):
|
|
42
42
|
|
43
43
|
class TestEmailColumn(TestCase):
|
44
44
|
def test_email(self):
|
45
|
-
class
|
45
|
+
class Manager(Table):
|
46
46
|
email = Email()
|
47
47
|
|
48
|
-
pydantic_model = create_pydantic_model(table=
|
48
|
+
pydantic_model = create_pydantic_model(table=Manager)
|
49
49
|
|
50
50
|
self.assertEqual(
|
51
51
|
pydantic_model.model_json_schema()["properties"]["email"]["anyOf"][
|
@@ -67,28 +67,28 @@ class TestNumericColumn(TestCase):
|
|
67
67
|
"""
|
68
68
|
|
69
69
|
def test_numeric_digits(self):
|
70
|
-
class
|
71
|
-
|
70
|
+
class Band(Table):
|
71
|
+
royalties = Numeric(digits=(5, 1))
|
72
72
|
|
73
|
-
pydantic_model = create_pydantic_model(table=
|
73
|
+
pydantic_model = create_pydantic_model(table=Band)
|
74
74
|
|
75
75
|
with self.assertRaises(ValidationError):
|
76
76
|
# This should fail as there are too much numbers after the decimal
|
77
77
|
# point
|
78
|
-
pydantic_model(
|
78
|
+
pydantic_model(royalties=decimal.Decimal("1.11"))
|
79
79
|
|
80
80
|
with self.assertRaises(ValidationError):
|
81
81
|
# This should fail as there are too much numbers in total
|
82
|
-
pydantic_model(
|
82
|
+
pydantic_model(royalties=decimal.Decimal("11111.1"))
|
83
83
|
|
84
|
-
pydantic_model(
|
84
|
+
pydantic_model(royalties=decimal.Decimal("1.0"))
|
85
85
|
|
86
86
|
def test_numeric_without_digits(self):
|
87
|
-
class
|
88
|
-
|
87
|
+
class Band(Table):
|
88
|
+
royalties = Numeric()
|
89
89
|
|
90
90
|
try:
|
91
|
-
create_pydantic_model(table=
|
91
|
+
create_pydantic_model(table=Band)
|
92
92
|
except TypeError:
|
93
93
|
self.fail(
|
94
94
|
"Creating numeric field without"
|
@@ -297,13 +297,13 @@ class TestColumnHelpText(TestCase):
|
|
297
297
|
def test_column_help_text_present(self):
|
298
298
|
help_text = "In millions of US dollars."
|
299
299
|
|
300
|
-
class
|
301
|
-
|
300
|
+
class Band(Table):
|
301
|
+
royalties = Numeric(digits=(5, 1), help_text=help_text)
|
302
302
|
|
303
|
-
pydantic_model = create_pydantic_model(table=
|
303
|
+
pydantic_model = create_pydantic_model(table=Band)
|
304
304
|
|
305
305
|
self.assertEqual(
|
306
|
-
pydantic_model.model_json_schema()["properties"]["
|
306
|
+
pydantic_model.model_json_schema()["properties"]["royalties"][
|
307
307
|
"extra"
|
308
308
|
]["help_text"],
|
309
309
|
help_text,
|
@@ -317,12 +317,12 @@ class TestTableHelpText(TestCase):
|
|
317
317
|
"""
|
318
318
|
|
319
319
|
def test_table_help_text_present(self):
|
320
|
-
help_text = "
|
320
|
+
help_text = "Bands playing concerts."
|
321
321
|
|
322
|
-
class
|
322
|
+
class Band(Table, help_text=help_text):
|
323
323
|
name = Varchar()
|
324
324
|
|
325
|
-
pydantic_model = create_pydantic_model(table=
|
325
|
+
pydantic_model = create_pydantic_model(table=Band)
|
326
326
|
|
327
327
|
self.assertEqual(
|
328
328
|
pydantic_model.model_json_schema()["extra"]["help_text"],
|
@@ -332,10 +332,10 @@ class TestTableHelpText(TestCase):
|
|
332
332
|
|
333
333
|
class TestUniqueColumn(TestCase):
|
334
334
|
def test_unique_column_true(self):
|
335
|
-
class
|
335
|
+
class Manager(Table):
|
336
336
|
name = Varchar(unique=True)
|
337
337
|
|
338
|
-
pydantic_model = create_pydantic_model(table=
|
338
|
+
pydantic_model = create_pydantic_model(table=Manager)
|
339
339
|
|
340
340
|
self.assertEqual(
|
341
341
|
pydantic_model.model_json_schema()["properties"]["name"]["extra"][
|
@@ -345,10 +345,10 @@ class TestUniqueColumn(TestCase):
|
|
345
345
|
)
|
346
346
|
|
347
347
|
def test_unique_column_false(self):
|
348
|
-
class
|
348
|
+
class Manager(Table):
|
349
349
|
name = Varchar()
|
350
350
|
|
351
|
-
pydantic_model = create_pydantic_model(table=
|
351
|
+
pydantic_model = create_pydantic_model(table=Manager)
|
352
352
|
|
353
353
|
self.assertEqual(
|
354
354
|
pydantic_model.model_json_schema()["properties"]["name"]["extra"][
|
@@ -360,48 +360,66 @@ class TestUniqueColumn(TestCase):
|
|
360
360
|
|
361
361
|
class TestJSONColumn(TestCase):
|
362
362
|
def test_default(self):
|
363
|
-
class
|
364
|
-
|
365
|
-
|
363
|
+
class Studio(Table):
|
364
|
+
facilities = JSON()
|
365
|
+
facilities_b = JSONB()
|
366
366
|
|
367
|
-
pydantic_model = create_pydantic_model(table=
|
367
|
+
pydantic_model = create_pydantic_model(table=Studio)
|
368
368
|
|
369
|
-
json_string = '{"
|
369
|
+
json_string = '{"guitar_amps": 6}'
|
370
370
|
|
371
|
-
model_instance = pydantic_model(
|
372
|
-
|
373
|
-
|
371
|
+
model_instance = pydantic_model(
|
372
|
+
facilities=json_string, facilities_b=json_string
|
373
|
+
)
|
374
|
+
self.assertEqual(
|
375
|
+
model_instance.facilities,
|
376
|
+
json_string,
|
377
|
+
)
|
378
|
+
self.assertEqual(
|
379
|
+
model_instance.facilities_b,
|
380
|
+
json_string,
|
381
|
+
)
|
374
382
|
|
375
383
|
def test_deserialize_json(self):
|
376
|
-
class
|
377
|
-
|
378
|
-
|
384
|
+
class Studio(Table):
|
385
|
+
facilities = JSON()
|
386
|
+
facilities_b = JSONB()
|
379
387
|
|
380
388
|
pydantic_model = create_pydantic_model(
|
381
|
-
table=
|
389
|
+
table=Studio, deserialize_json=True
|
382
390
|
)
|
383
391
|
|
384
|
-
json_string = '{"
|
385
|
-
output = {"
|
392
|
+
json_string = '{"guitar_amps": 6}'
|
393
|
+
output = {"guitar_amps": 6}
|
386
394
|
|
387
|
-
model_instance = pydantic_model(
|
388
|
-
|
389
|
-
|
395
|
+
model_instance = pydantic_model(
|
396
|
+
facilities=json_string, facilities_b=json_string
|
397
|
+
)
|
398
|
+
self.assertEqual(
|
399
|
+
model_instance.facilities,
|
400
|
+
output,
|
401
|
+
)
|
402
|
+
self.assertEqual(
|
403
|
+
model_instance.facilities_b,
|
404
|
+
output,
|
405
|
+
)
|
390
406
|
|
391
407
|
def test_validation(self):
|
392
|
-
class
|
393
|
-
|
394
|
-
|
408
|
+
class Studio(Table):
|
409
|
+
facilities = JSON()
|
410
|
+
facilities_b = JSONB()
|
395
411
|
|
396
412
|
for deserialize_json in (True, False):
|
397
413
|
pydantic_model = create_pydantic_model(
|
398
|
-
table=
|
414
|
+
table=Studio, deserialize_json=deserialize_json
|
399
415
|
)
|
400
416
|
|
401
417
|
json_string = "error"
|
402
418
|
|
403
419
|
with self.assertRaises(pydantic.ValidationError):
|
404
|
-
pydantic_model(
|
420
|
+
pydantic_model(
|
421
|
+
facilities=json_string, facilities_b=json_string
|
422
|
+
)
|
405
423
|
|
406
424
|
def test_json_widget(self):
|
407
425
|
"""
|
@@ -409,112 +427,112 @@ class TestJSONColumn(TestCase):
|
|
409
427
|
special widget in Piccolo Admin.
|
410
428
|
"""
|
411
429
|
|
412
|
-
class
|
413
|
-
|
430
|
+
class Studio(Table):
|
431
|
+
facilities = JSON()
|
414
432
|
|
415
|
-
pydantic_model = create_pydantic_model(table=
|
433
|
+
pydantic_model = create_pydantic_model(table=Studio)
|
416
434
|
|
417
435
|
self.assertEqual(
|
418
|
-
pydantic_model.model_json_schema()["properties"]["
|
436
|
+
pydantic_model.model_json_schema()["properties"]["facilities"][
|
419
437
|
"extra"
|
420
438
|
]["widget"],
|
421
439
|
"json",
|
422
440
|
)
|
423
441
|
|
424
442
|
def test_null_value(self):
|
425
|
-
class
|
426
|
-
|
427
|
-
|
443
|
+
class Studio(Table):
|
444
|
+
facilities = JSON(null=True)
|
445
|
+
facilities_b = JSONB(null=True)
|
428
446
|
|
429
|
-
pydantic_model = create_pydantic_model(table=
|
430
|
-
movie = pydantic_model(
|
447
|
+
pydantic_model = create_pydantic_model(table=Studio)
|
448
|
+
movie = pydantic_model(facilities=None, facilities_b=None)
|
431
449
|
|
432
|
-
self.assertIsNone(movie.
|
433
|
-
self.assertIsNone(movie.
|
450
|
+
self.assertIsNone(movie.facilities)
|
451
|
+
self.assertIsNone(movie.facilities_b)
|
434
452
|
|
435
453
|
|
436
454
|
class TestExcludeColumns(TestCase):
|
437
455
|
def test_all(self):
|
438
|
-
class
|
439
|
-
|
440
|
-
|
456
|
+
class Band(Table):
|
457
|
+
name = Varchar()
|
458
|
+
bio = Text()
|
441
459
|
|
442
|
-
pydantic_model = create_pydantic_model(
|
460
|
+
pydantic_model = create_pydantic_model(Band, exclude_columns=())
|
443
461
|
|
444
462
|
properties = pydantic_model.model_json_schema()["properties"]
|
445
|
-
self.assertIsInstance(properties["
|
446
|
-
self.assertIsInstance(properties["
|
463
|
+
self.assertIsInstance(properties["name"], dict)
|
464
|
+
self.assertIsInstance(properties["bio"], dict)
|
447
465
|
|
448
466
|
def test_exclude(self):
|
449
|
-
class
|
450
|
-
|
451
|
-
|
467
|
+
class Band(Table):
|
468
|
+
name = Varchar()
|
469
|
+
album = Varchar()
|
452
470
|
|
453
471
|
pydantic_model = create_pydantic_model(
|
454
|
-
|
455
|
-
exclude_columns=(
|
472
|
+
Band,
|
473
|
+
exclude_columns=(Band.name,),
|
456
474
|
)
|
457
475
|
|
458
476
|
properties = pydantic_model.model_json_schema()["properties"]
|
459
|
-
self.assertIsInstance(properties.get("
|
460
|
-
self.assertIsNone(properties.get("
|
477
|
+
self.assertIsInstance(properties.get("album"), dict)
|
478
|
+
self.assertIsNone(properties.get("dict"))
|
461
479
|
|
462
480
|
def test_exclude_all_manually(self):
|
463
|
-
class
|
464
|
-
|
465
|
-
|
481
|
+
class Band(Table):
|
482
|
+
name = Varchar()
|
483
|
+
album = Varchar()
|
466
484
|
|
467
485
|
pydantic_model = create_pydantic_model(
|
468
|
-
|
469
|
-
exclude_columns=(
|
486
|
+
Band,
|
487
|
+
exclude_columns=(Band.name, Band.album),
|
470
488
|
)
|
471
489
|
|
472
490
|
self.assertEqual(pydantic_model.model_json_schema()["properties"], {})
|
473
491
|
|
474
492
|
def test_exclude_all_meta(self):
|
475
|
-
class
|
476
|
-
|
477
|
-
|
493
|
+
class Band(Table):
|
494
|
+
name = Varchar()
|
495
|
+
album = Varchar()
|
478
496
|
|
479
497
|
pydantic_model = create_pydantic_model(
|
480
|
-
|
481
|
-
exclude_columns=tuple(
|
498
|
+
Band,
|
499
|
+
exclude_columns=tuple(Band._meta.columns),
|
482
500
|
)
|
483
501
|
|
484
502
|
self.assertEqual(pydantic_model.model_json_schema()["properties"], {})
|
485
503
|
|
486
504
|
def test_invalid_column_str(self):
|
487
|
-
class
|
488
|
-
|
489
|
-
|
505
|
+
class Band(Table):
|
506
|
+
name = Varchar()
|
507
|
+
album = Varchar()
|
490
508
|
|
491
509
|
with self.assertRaises(ValueError):
|
492
510
|
create_pydantic_model(
|
493
|
-
|
494
|
-
exclude_columns=("
|
511
|
+
Band,
|
512
|
+
exclude_columns=("album",),
|
495
513
|
)
|
496
514
|
|
497
515
|
def test_invalid_column_different_table(self):
|
498
|
-
class
|
499
|
-
|
500
|
-
|
516
|
+
class Band(Table):
|
517
|
+
name = Varchar()
|
518
|
+
album = Varchar()
|
501
519
|
|
502
|
-
class
|
503
|
-
|
520
|
+
class Band2(Table):
|
521
|
+
photo = Varchar()
|
504
522
|
|
505
523
|
with self.assertRaises(ValueError):
|
506
|
-
create_pydantic_model(
|
524
|
+
create_pydantic_model(Band, exclude_columns=(Band2.photo,))
|
507
525
|
|
508
526
|
def test_invalid_column_different_table_same_type(self):
|
509
|
-
class
|
510
|
-
|
511
|
-
|
527
|
+
class Band(Table):
|
528
|
+
name = Varchar()
|
529
|
+
album = Varchar()
|
512
530
|
|
513
|
-
class
|
514
|
-
|
531
|
+
class Band2(Table):
|
532
|
+
name = Varchar()
|
515
533
|
|
516
534
|
with self.assertRaises(ValueError):
|
517
|
-
create_pydantic_model(
|
535
|
+
create_pydantic_model(Band, exclude_columns=(Band2.name,))
|
518
536
|
|
519
537
|
def test_exclude_nested(self):
|
520
538
|
class Manager(Table):
|
@@ -892,7 +910,7 @@ class TestDBColumnName(TestCase):
|
|
892
910
|
|
893
911
|
model = BandModel(regrettable_column_name="test")
|
894
912
|
|
895
|
-
self.assertEqual(model.name, "test")
|
913
|
+
self.assertEqual(model.name, "test")
|
896
914
|
|
897
915
|
|
898
916
|
class TestJSONSchemaExtra(TestCase):
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|