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.
@@ -1,11 +1,10 @@
1
- import asyncio
2
- import typing as t
1
+ from typing import Any
3
2
 
4
3
  from hypercorn.middleware import DispatcherMiddleware
5
4
  from piccolo.engine import engine_finder
6
5
  from piccolo_admin.endpoints import create_admin
7
6
  from piccolo_api.crud.serializers import create_pydantic_model
8
- from sanic import Request, Sanic, json
7
+ from sanic import NotFound, Request, Sanic, json
9
8
  from sanic_ext import openapi
10
9
 
11
10
  from home.endpoints import index
@@ -16,17 +15,24 @@ app = Sanic(__name__)
16
15
  app.static("/static/", "static")
17
16
 
18
17
 
19
- TaskModelIn: t.Any = create_pydantic_model(
18
+ TaskModelIn: Any = create_pydantic_model(
20
19
  table=Task,
21
20
  model_name="TaskModelIn",
22
21
  )
23
- TaskModelOut: t.Any = create_pydantic_model(
22
+ TaskModelOut: Any = create_pydantic_model(
24
23
  table=Task,
25
24
  include_default_columns=True,
26
25
  model_name="TaskModelOut",
27
26
  )
28
27
 
29
28
 
29
+ # Check if the record is None. Use for query callback
30
+ def check_record_not_found(result: dict[str, Any]) -> dict[str, Any]:
31
+ if result is None:
32
+ raise NotFound(message="Record not found")
33
+ return result
34
+
35
+
30
36
  @app.get("/")
31
37
  @openapi.exclude()
32
38
  def home(request: Request):
@@ -43,6 +49,19 @@ async def tasks(request: Request):
43
49
  )
44
50
 
45
51
 
52
+ @app.get("/tasks/<task_id:int>/")
53
+ @openapi.tag("Task")
54
+ @openapi.response(200, {"application/json": TaskModelOut.model_json_schema()})
55
+ async def single_task(request: Request, task_id: int):
56
+ task = (
57
+ await Task.select()
58
+ .where(Task._meta.primary_key == task_id)
59
+ .first()
60
+ .callback(check_record_not_found)
61
+ )
62
+ return json(task, status=200)
63
+
64
+
46
65
  @app.post("/tasks/")
47
66
  @openapi.definition(
48
67
  body={"application/json": TaskModelIn.model_json_schema()},
@@ -62,9 +81,11 @@ async def create_task(request: Request):
62
81
  )
63
82
  @openapi.response(200, {"application/json": TaskModelOut.model_json_schema()})
64
83
  async def update_task(request: Request, task_id: int):
65
- task = await Task.objects().get(Task._meta.primary_key == task_id)
66
- if not task:
67
- return json({}, status=404)
84
+ task = (
85
+ await Task.objects()
86
+ .get(Task._meta.primary_key == task_id)
87
+ .callback(check_record_not_found)
88
+ )
68
89
  for key, value in request.json.items():
69
90
  setattr(task, key, value)
70
91
 
@@ -75,9 +96,11 @@ async def update_task(request: Request, task_id: int):
75
96
  @app.delete("/tasks/<task_id:int>/")
76
97
  @openapi.tag("Task")
77
98
  async def delete_task(request: Request, task_id: int):
78
- task = await Task.objects().get(Task._meta.primary_key == task_id)
79
- if not task:
80
- return json({}, status=404)
99
+ task = (
100
+ await Task.objects()
101
+ .get(Task._meta.primary_key == task_id)
102
+ .callback(check_record_not_found)
103
+ )
81
104
  await task.remove()
82
105
  return json({}, status=200)
83
106
 
@@ -12,6 +12,7 @@ from typing import Optional
12
12
 
13
13
  from piccolo.columns import (
14
14
  JSON,
15
+ M2M,
15
16
  UUID,
16
17
  Array,
17
18
  Boolean,
@@ -19,6 +20,7 @@ from piccolo.columns import (
19
20
  ForeignKey,
20
21
  Integer,
21
22
  Interval,
23
+ LazyTableReference,
22
24
  Numeric,
23
25
  Serial,
24
26
  Text,
@@ -49,6 +51,7 @@ class Band(Table):
49
51
  name = Varchar(length=50)
50
52
  manager = ForeignKey(references=Manager, null=True)
51
53
  popularity = Integer()
54
+ genres = M2M(LazyTableReference("GenreToBand", module_path=__name__))
52
55
 
53
56
  @classmethod
54
57
  def get_readable(cls) -> Readable:
@@ -161,6 +164,26 @@ class Album(Table):
161
164
  )
162
165
 
163
166
 
167
+ class Genre(Table):
168
+ id: Serial
169
+ name = Varchar()
170
+ bands = M2M(LazyTableReference("GenreToBand", module_path=__name__))
171
+
172
+ @classmethod
173
+ def get_readable(cls) -> Readable:
174
+ return Readable(
175
+ template="%s",
176
+ columns=[cls.name],
177
+ )
178
+
179
+
180
+ class GenreToBand(Table):
181
+ id: Serial
182
+ band = ForeignKey(Band)
183
+ genre = ForeignKey(Genre)
184
+ reason = Text(null=True, default=None)
185
+
186
+
164
187
  TABLES = (
165
188
  Manager,
166
189
  Band,
@@ -171,6 +194,8 @@ TABLES = (
171
194
  DiscountCode,
172
195
  RecordingStudio,
173
196
  Album,
197
+ Genre,
198
+ GenreToBand,
174
199
  )
175
200
 
176
201
 
@@ -282,6 +307,24 @@ def populate():
282
307
  ),
283
308
  ).run_sync()
284
309
 
310
+ genres = Genre.insert(
311
+ Genre(name="Rock"),
312
+ Genre(name="Classical"),
313
+ Genre(name="Folk"),
314
+ ).run_sync()
315
+
316
+ GenreToBand.insert(
317
+ GenreToBand(
318
+ band=pythonistas.id,
319
+ genre=genres[0]["id"],
320
+ reason="Because they rock.",
321
+ ),
322
+ GenreToBand(band=pythonistas.id, genre=genres[2]["id"]),
323
+ GenreToBand(band=rustaceans.id, genre=genres[2]["id"]),
324
+ GenreToBand(band=c_sharps.id, genre=genres[0]["id"]),
325
+ GenreToBand(band=c_sharps.id, genre=genres[1]["id"]),
326
+ ).run_sync()
327
+
285
328
 
286
329
  def run(
287
330
  engine: str = "sqlite",
@@ -158,8 +158,8 @@ class MathDelegate:
158
158
  raise ValueError(
159
159
  "Adding values across joins isn't currently supported."
160
160
  )
161
- column_name = column._meta.db_column_name
162
- return QueryString(f"{column_name} {operator} {column_name}")
161
+ other_column_name = value._meta.db_column_name
162
+ return QueryString(f"{column_name} {operator} {other_column_name}")
163
163
  elif isinstance(value, (int, float)):
164
164
  if reverse:
165
165
  return QueryString(f"{{}} {operator} {column_name}", value)
piccolo/table.py CHANGED
@@ -16,7 +16,6 @@ from piccolo.columns.column_types import (
16
16
  Email,
17
17
  ForeignKey,
18
18
  ReferencedTable,
19
- Secret,
20
19
  Serial,
21
20
  )
22
21
  from piccolo.columns.defaults.base import Default
@@ -84,7 +83,7 @@ class TableMeta:
84
83
  foreign_key_columns: list[ForeignKey] = field(default_factory=list)
85
84
  primary_key: Column = field(default_factory=Column)
86
85
  json_columns: list[Union[JSON, JSONB]] = field(default_factory=list)
87
- secret_columns: list[Secret] = field(default_factory=list)
86
+ secret_columns: list[Column] = field(default_factory=list)
88
87
  auto_update_columns: list[Column] = field(default_factory=list)
89
88
  tags: list[str] = field(default_factory=list)
90
89
  help_text: Optional[str] = None
@@ -274,7 +273,7 @@ class Table(metaclass=TableMetaclass):
274
273
  non_default_columns: list[Column] = []
275
274
  array_columns: list[Array] = []
276
275
  foreign_key_columns: list[ForeignKey] = []
277
- secret_columns: list[Secret] = []
276
+ secret_columns: list[Column] = []
278
277
  json_columns: list[Union[JSON, JSONB]] = []
279
278
  email_columns: list[Email] = []
280
279
  auto_update_columns: list[Column] = []
@@ -315,15 +314,15 @@ class Table(metaclass=TableMetaclass):
315
314
  if isinstance(column, Email):
316
315
  email_columns.append(column)
317
316
 
318
- if isinstance(column, Secret):
319
- secret_columns.append(column)
320
-
321
317
  if isinstance(column, ForeignKey):
322
318
  foreign_key_columns.append(column)
323
319
 
324
320
  if isinstance(column, (JSON, JSONB)):
325
321
  json_columns.append(column)
326
322
 
323
+ if column._meta.secret:
324
+ secret_columns.append(column)
325
+
327
326
  if column._meta.auto_update is not ...:
328
327
  auto_update_columns.append(column)
329
328
 
@@ -1373,6 +1372,15 @@ class Table(metaclass=TableMetaclass):
1373
1372
  columns.append(
1374
1373
  f"{col._meta.name} = {col.__class__.__name__}({params_string})"
1375
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
+
1376
1384
  columns_string = spacer.join(columns)
1377
1385
  tablename = repr(cls._meta.tablename)
1378
1386
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime
4
+ import decimal
4
5
  import json
5
6
  from collections.abc import Callable
6
7
  from decimal import Decimal
@@ -20,6 +21,7 @@ class ModelBuilder:
20
21
  datetime.date: RandomBuilder.next_date,
21
22
  datetime.datetime: RandomBuilder.next_datetime,
22
23
  float: RandomBuilder.next_float,
24
+ decimal.Decimal: RandomBuilder.next_decimal,
23
25
  int: RandomBuilder.next_int,
24
26
  str: RandomBuilder.next_str,
25
27
  datetime.time: RandomBuilder.next_time,
@@ -163,8 +165,8 @@ class ModelBuilder:
163
165
  random_value: Any
164
166
  if column.value_type == Decimal:
165
167
  precision, scale = column._meta.params["digits"] or (4, 2)
166
- random_value = RandomBuilder.next_float(
167
- maximum=10 ** (precision - scale), scale=scale
168
+ random_value = RandomBuilder.next_decimal(
169
+ precision=precision, scale=scale
168
170
  )
169
171
  elif column.value_type == datetime.datetime:
170
172
  tz_aware = getattr(column, "tz_aware", False)
@@ -1,4 +1,5 @@
1
1
  import datetime
2
+ import decimal
2
3
  import enum
3
4
  import random
4
5
  import string
@@ -43,6 +44,17 @@ class RandomBuilder:
43
44
  def next_float(cls, minimum=0, maximum=2147483647, scale=5) -> float:
44
45
  return round(random.uniform(minimum, maximum), scale)
45
46
 
47
+ @classmethod
48
+ def next_decimal(
49
+ cls, precision: int = 4, scale: int = 2
50
+ ) -> decimal.Decimal:
51
+ # For precision 4 and scale 2, maximum needs to be 99.99.
52
+ maximum = (10 ** (precision - scale)) - (10 ** (-1 * scale))
53
+ float_number = cls.next_float(maximum=maximum, scale=scale)
54
+ # We convert float_number to a string first, otherwise the decimal
55
+ # value is slightly off due to floating point precision.
56
+ return decimal.Decimal(str(float_number))
57
+
46
58
  @classmethod
47
59
  def next_int(cls, minimum=0, maximum=2147483647) -> int:
48
60
  return random.randint(minimum, maximum)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piccolo
3
- Version: 1.28.0
3
+ Version: 1.29.0
4
4
  Summary: A fast, user friendly ORM and query builder which supports asyncio.
5
5
  Home-page: https://github.com/piccolo-orm/piccolo
6
6
  Author: Daniel Townsend
@@ -1,10 +1,10 @@
1
- piccolo/__init__.py,sha256=xu4CW3LmhKRfjg47hg5s7XZDPYKw_3YQKCjwvwcpK1E,23
1
+ piccolo/__init__.py,sha256=C-iTYm1gDGgB27Giaf3CpBiC_O-eM1faeVO4P3hLU68,23
2
2
  piccolo/custom_types.py,sha256=3ceuQZNMQTXVikZzACnJS4eIIgTpwubKg2kaKOshprw,1035
3
3
  piccolo/main.py,sha256=1VsFV67FWTUikPTysp64Fmgd9QBVa_9wcwKfwj2UCEA,5117
4
4
  piccolo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  piccolo/querystring.py,sha256=nOSCh5sTng-dxHr5WdUqf7TwYTrKAgkEP5GcrBmcW9I,10011
6
6
  piccolo/schema.py,sha256=2S5SlG9FfAuRKNLA6cADziNcFJ1PeNj5uhc_NJ-_g0I,7953
7
- piccolo/table.py,sha256=83FMLo3AuR_eePhbu1YXXQPHcJ84n5XaQOys6zesgoU,50438
7
+ piccolo/table.py,sha256=-i932n96luHZd2UJ0-qboky_DQaoM6xaRAvYt9HeXdE,50721
8
8
  piccolo/table_reflection.py,sha256=q3Dp70SI0r8eNduOgVv4u4_W5NwN6A54oYB_2BnyaVY,7535
9
9
  piccolo/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  piccolo/apps/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -17,16 +17,16 @@ piccolo/apps/app/commands/templates/tables.py.jinja,sha256=revzdrvDDwe78VedBKz0z
17
17
  piccolo/apps/asgi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  piccolo/apps/asgi/piccolo_app.py,sha256=7VUvqQJbB-ScO0A62S6MiJmQL9F5DS-SdlqlDLbAblE,217
19
19
  piccolo/apps/asgi/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- piccolo/apps/asgi/commands/new.py,sha256=6TNnp4Dz4g8tZNsXPv-cMCchRhmFzkUrwXZtiQ6ima8,4314
20
+ piccolo/apps/asgi/commands/new.py,sha256=DiwR49zIvTSfsByaCjqleWD38UjHsWUE8hIf2C7DICA,4320
21
21
  piccolo/apps/asgi/commands/templates/app/README.md.jinja,sha256=As3gNEZt9qcRmTVkjCzNtXJ8r4-3g0fCSe7Q-P39ezI,214
22
- piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja,sha256=ERbVPjjLQWDhXBYzZ_h3WT1Uf0qRL19rFE_NO3i09G0,3213
23
- piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja,sha256=fIbD106Us4j2DBv_41PimvFRVw2FuZhOfc3xFPkNjuY,2715
24
- piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja,sha256=LOn3auJFeXNW48rtHzRbH3MzxWbRNhFib6Fm6wDS53E,1684
25
- piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja,sha256=mKnYfUOnYyWJA1jFoRLCUOGQlK6imaxx_1qaauGjeeQ,2627
22
+ piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja,sha256=xumVDAcbnVoaAcslDyUyx8MQU2qghB7LnJR-8IJAhSc,3824
23
+ piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja,sha256=clKi85iqujPdP0M88QjocBme17zxKB4G5-Ay4SMzVbg,3433
24
+ piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja,sha256=puFcf7-f9qR3HKWaRnBVgtpHZUiVFCo3MsGxx2nCQVQ,3241
25
+ piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja,sha256=NjKzww8-hvqVXdaXdjEF4kTm3tM2huOHUQRra_2LFJU,3355
26
26
  piccolo/apps/asgi/commands/templates/app/_lilya_app.py.jinja,sha256=PUph5Jj_AXVpxXZmpUzzHXogUchU8vjKBL_7WvgrfCU,1260
27
- piccolo/apps/asgi/commands/templates/app/_litestar_app.py.jinja,sha256=VCY4FoA7YlEhtjWB09XWQqi8GgL36VQwGGBpSXUDO5o,3349
28
- piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja,sha256=3LoQJ6LWRB0NFIcfQtPUdNWb10csyDGgCIa2zx8w4e8,2837
29
- piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja,sha256=qza84-aV6wnPlPQ9bpcD5DLO-pvkmoSnb_lXmcymv6c,3159
27
+ piccolo/apps/asgi/commands/templates/app/_litestar_app.py.jinja,sha256=dFcVfO150uj9OfdJ-3-MVZ-3RCoLIp9AOFdn1qSQzbE,3872
28
+ piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja,sha256=4CACs2sjHhDF3TpxM232F6hq744WD-ue8hxFMAUd45E,3468
29
+ piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja,sha256=voq09eUymx4o7pMS7eoXlSrJYR8LY-HLW5Ufi4Utc2w,3781
30
30
  piccolo/apps/asgi/commands/templates/app/_starlette_app.py.jinja,sha256=vHcAzsS9I3OevYoznwZp8zucI4OEyUjj-EOAtscmlSE,1443
31
31
  piccolo/apps/asgi/commands/templates/app/app.py.jinja,sha256=n2KriWxCnq65vdEvX1USTqZPDbNkYXQqTJT5EmespT8,667
32
32
  piccolo/apps/asgi/commands/templates/app/conftest.py.jinja,sha256=ZG1pRVMv3LhIfOsO3_08c_fF3EV4_EApuDHiIFFPJdk,497
@@ -83,7 +83,7 @@ piccolo/apps/migrations/commands/templates/migration.py.jinja,sha256=wMC8RTIcQj3
83
83
  piccolo/apps/playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
84
  piccolo/apps/playground/piccolo_app.py,sha256=zs6nGxt-lgUF8nEwI0uDTNZDKQqjZaNDH8le5RqrMNE,222
85
85
  piccolo/apps/playground/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
- piccolo/apps/playground/commands/run.py,sha256=5761m3xLcxR0uSeUSKgQ3V2Pr8LlGB0JAjhGTX9gCVo,9680
86
+ piccolo/apps/playground/commands/run.py,sha256=TOAHIEGwEB09ufoZvDztO8osSRzU6lE96tr2u9BxmWM,10811
87
87
  piccolo/apps/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
88
  piccolo/apps/project/piccolo_app.py,sha256=mT3O0m3QcCfS0oOr3jt0QZ9TX6gUavGPjJeNn2C_fdM,220
89
89
  piccolo/apps/project/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -123,7 +123,7 @@ piccolo/apps/user/piccolo_migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
123
123
  piccolo/columns/__init__.py,sha256=OYhO_n9anMiU9nL-K6ATq9FhAtm8RyMpqYQ7fTVbhxI,1120
124
124
  piccolo/columns/base.py,sha256=66hsxdiPJQiJ-v0TRSwxUYx3Cbw4BUp84jr6EFaDoOc,33632
125
125
  piccolo/columns/choices.py,sha256=ij5cMmslGr0WcFjcFelhdECX1E3n3R7Kl2Tn5bhu_Lc,725
126
- piccolo/columns/column_types.py,sha256=tzMdgrVgr1iYsFrc85wcMmT0_MYZRRcbdhH11cfZyxc,88240
126
+ piccolo/columns/column_types.py,sha256=6Yg6F61u7VtAVchrokSO5Y5qYLR6FmPJ1ZBWzx27t7c,88251
127
127
  piccolo/columns/combination.py,sha256=lTxbrmUCrk_mVsvBPSEV7v72_dPjMs-Y8-MmAJFAifY,7088
128
128
  piccolo/columns/indexes.py,sha256=NfNok3v_791jgDlN28KmhP9ZCjl6031BXmjxV3ovXJk,372
129
129
  piccolo/columns/m2m.py,sha256=FfMS9oBZSrFwHsWxp8lJDj4JD0ukphYa3RPyJDKYyLY,14587
@@ -183,8 +183,8 @@ piccolo/query/methods/update.py,sha256=sRSoHU3SMSQQUbScSam8M4lin0RTBA6LrGQA-d4HB
183
183
  piccolo/query/operators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
184
184
  piccolo/query/operators/json.py,sha256=GDLUrMDuHe6XKuzCmvYmkIUUqpQcM-8KMz-peSCLiX4,3182
185
185
  piccolo/testing/__init__.py,sha256=pRFSqRInfx95AakOq54atmvqoB-ue073q2aR8u8zR40,83
186
- piccolo/testing/model_builder.py,sha256=aHCGLC3cFqiEI8MHZ0kZW3w1pZe8X9V5KyLpP5xSvRA,6567
187
- piccolo/testing/random_builder.py,sha256=vBa42-q1BhiuudFo1ZhSDXXQP50ZsWXOugQMUw38OFw,2128
186
+ piccolo/testing/model_builder.py,sha256=6JTL8CSpNTwF29wAzU6GEG33pp8tLk8viqRNfNLhu1s,6623
187
+ piccolo/testing/random_builder.py,sha256=N_aY1SDBDP6YHGAmYIw-mjTCCwbqt9fPBKW-V0KZ_-c,2650
188
188
  piccolo/testing/test_case.py,sha256=VdC-1uclubtePTuxNQ-KOTF5ODaBdwzLbPuiYI5MD48,3280
189
189
  piccolo/utils/__init__.py,sha256=SDFFraauI9Op8dCRkreQv1dwUcab8Mi1eC-n0EwlTy8,36
190
190
  piccolo/utils/dictionary.py,sha256=mhyyhWwxTSxZcEUntgeQKsbrXD3f_KLdwa0NPzgI11k,1878
@@ -201,7 +201,7 @@ piccolo/utils/sync.py,sha256=oLBVx7K-KhxIOT9wRTAcfixQPMfMJTcnvlmBQM7lAzg,963
201
201
  piccolo/utils/warnings.py,sha256=W00-Qb8s2bTc9Xk4g4E5Xqp_aBQROVfFEh-RVSozwZg,1226
202
202
  piccolo/utils/graphlib/__init__.py,sha256=SUJ5Yh7LiRun3nkBuLUSVmGNHF6fANrxSoYan0mtYB0,200
203
203
  piccolo/utils/graphlib/_graphlib.py,sha256=9FNGDSmTIEAk86FktniCe_J2yXjSE_sRZHDBAJJAUOw,9677
204
- piccolo-1.28.0.dist-info/licenses/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
204
+ piccolo-1.29.0.dist-info/licenses/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
205
205
  profiling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
206
206
  profiling/run_profile.py,sha256=264qsSFu93NTpExePnKQ9GkcN5fiuRBQ72WOSt0ZHck,829
207
207
  tests/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -285,7 +285,7 @@ tests/columns/test_timestamptz.py,sha256=P7zblPC6Fjjdk6iOhVUGIKnFFzbbUPVNSY98qbu
285
285
  tests/columns/test_uuid.py,sha256=taFYNvRZjQztMPbTQHYtwQutvcLnKPt6_aUxsf2o04Q,372
286
286
  tests/columns/test_varchar.py,sha256=fbwBdimHoGaylfrqkFIgQ5m2q80umSoUNHIwofM6j_c,721
287
287
  tests/columns/m2m/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
288
- tests/columns/m2m/base.py,sha256=CCq52vf1_jyIRjn71i_tEhY_eW_8FKJVLSHgUSfYRjw,15023
288
+ tests/columns/m2m/base.py,sha256=_EB5j2jFnSYgvGDT_skdvvZL-L5kcQ6CZCDqpSEGVjY,14067
289
289
  tests/columns/m2m/test_m2m.py,sha256=0ObmIHUJF6CZoNBznc5xXVr5_BbGBqOmWwtpg8IcPt4,13055
290
290
  tests/columns/m2m/test_m2m_schema.py,sha256=oxu7eAjFFpDjnq9Eq-5OTNmlnsEIMFWx18OItfpVs-s,339
291
291
  tests/conf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -346,7 +346,7 @@ tests/table/test_inheritance.py,sha256=AAkhEhhixVGweGe7ckzj-yypW-cj6D88Cca4-pjkw
346
346
  tests/table/test_insert.py,sha256=c7hJ1SsTiW43l_Z5KHSN3894ICzttOAsTfWN9rUOl0k,13696
347
347
  tests/table/test_join.py,sha256=Ukgvjc8NweBGHM7fVFytGQYG9P9thRaMeEvWXYs2Qes,15910
348
348
  tests/table/test_join_on.py,sha256=cdAV39JwHi0kIas2p9cw7mcsUv6mKLZD--_SUA0zLfI,2771
349
- tests/table/test_metaclass.py,sha256=pMv0PHh-2a9p74bweQXCXnq1OFsJ7Gk0uWRFdCTMf58,4123
349
+ tests/table/test_metaclass.py,sha256=zIlUj0QfspHwnM3jb1DL_-7KTImBkHhjsJuShAvnuT8,4286
350
350
  tests/table/test_objects.py,sha256=ptbeIICOfZ2-Fl6K_Llo1SKIyQ5ngUZ-rZtgll5cMWM,9047
351
351
  tests/table/test_output.py,sha256=ZnpPbgVp79JcB6E_ooWQxOpOlhkwNUlMxC-1LSIEc2Y,4304
352
352
  tests/table/test_raw.py,sha256=9PTvYngQi41nYd5lKzkJdTqsEcwrdOXcvZjq-W26CwQ,1683
@@ -354,9 +354,9 @@ tests/table/test_ref.py,sha256=eYNRnYHzNMXuMbV3B1ca5EidpIg4500q6hr1ccuVaso,269
354
354
  tests/table/test_refresh.py,sha256=yn36LV6wXuY6YuIutLgHFE4hl-LIRuWqwCY4WB-wzJg,9253
355
355
  tests/table/test_repr.py,sha256=uahz3_GffGQrf2mDE-4-Pu4AmSLBAyso6-9rbohCl58,446
356
356
  tests/table/test_select.py,sha256=C-6S9HIZxMQ9cKWmmG8yDXPR37btdt6AUoZDbvcTYJs,43017
357
- tests/table/test_str.py,sha256=eztWNULcjARR1fr9X5n4tojhDNgDfatVyNHwuYrzHAo,1731
357
+ tests/table/test_str.py,sha256=QCmx9Xc-pUXfAtXz9mPzMfW_WCajjydgz_YT1cYsgvY,1528
358
358
  tests/table/test_table_exists.py,sha256=upv2e9UD32V2QZOShzmcw0reMqRbYiX_jxWx57p25jg,1082
359
- tests/table/test_update.py,sha256=bWt4jMhv3S8fpars0_uW5N-Nq4No5sIKu3M-k0-LZFo,20505
359
+ tests/table/test_update.py,sha256=M950plqKFYtew_4fqx91E0tIrwQL3zZeZh2CmvGy9yw,20979
360
360
  tests/table/test_update_self.py,sha256=im6HcM-WLkEhZP0vTL42tYEJZyAZG6gDOxCnbikCBD4,907
361
361
  tests/table/instance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
362
  tests/table/instance/test_create.py,sha256=JD0l7L9dDK1FKPhUs6WC_B2bruPR1qQ8aIqXpEbfiUg,1105
@@ -368,7 +368,7 @@ tests/table/instance/test_save.py,sha256=ccdDz-bR3aYDa16_RGQP7JTXprgm1mT6-NpF1y3
368
368
  tests/table/instance/test_to_dict.py,sha256=gkiYkmcI5qcy5E-ERWWmO-Q8uyVSFfcpJ8d53LlzCuI,3442
369
369
  tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
370
370
  tests/testing/test_model_builder.py,sha256=S0rT7IAmXfrQ6vRQxmkleTrAmsO5fi-tw0SF0l4jBLw,6185
371
- tests/testing/test_random_builder.py,sha256=Upz9P1bhICVo0udI6Li-5eEdrXKbv8rMMLe0uK6pqB0,1694
371
+ tests/testing/test_random_builder.py,sha256=x2XOY2IAdIiC_hWWJ4iffLRiWOdn4CMvjiseeVvEYC8,1888
372
372
  tests/testing/test_test_case.py,sha256=qyDWYT44EZNyuWhaZXgSOpX48RaRw5u4FgNi87FYt2k,1691
373
373
  tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
374
374
  tests/utils/test_dictionary.py,sha256=GdWujlYQy6t09p2aQHPibkkPNbBYwkFwomKrVnztJTo,1480
@@ -377,13 +377,13 @@ tests/utils/test_lazy_loader.py,sha256=wDWhlV2IR6RuTaYCI5eWesz465WqIscYEJjwWnPrh
377
377
  tests/utils/test_list.py,sha256=25UoStmrYS_JDOKsXYqTDc3FUkOe3dUcy51r0I6grK0,769
378
378
  tests/utils/test_naming.py,sha256=ncJdzkMHSVFo2YQLkRhY93WJ8_W_j2pW9tHHL_ZgQcs,661
379
379
  tests/utils/test_printing.py,sha256=W8pQyWX2pEkLdMYWxtibFXJnI4MVsb6XMOTo3DZUuZc,668
380
- tests/utils/test_pydantic.py,sha256=fS3rd3rbn54uBN9w22n3-sCcvAMtk3dq54KMo2evMtQ,27726
380
+ tests/utils/test_pydantic.py,sha256=xjob1PgQphzbSdLQNNkzzZ5XUDdUpC4HAiEj1kHO4tw,27881
381
381
  tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5E,2535
382
382
  tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
383
383
  tests/utils/test_table_reflection.py,sha256=bIlwIupyD6XLupf7KYZ1le4EPdft7Jp2ofwF2PHqn60,3771
384
384
  tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
385
- piccolo-1.28.0.dist-info/METADATA,sha256=WINpG0xS2r9wGPE2tg3sWgFWiFCeBHqSf-WuJN5La-4,5531
386
- piccolo-1.28.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
387
- piccolo-1.28.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
388
- piccolo-1.28.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
389
- piccolo-1.28.0.dist-info/RECORD,,
385
+ piccolo-1.29.0.dist-info/METADATA,sha256=KgAnQBJETmXrcDB97hFPLSdD08sjQymkUifFBakE56Y,5531
386
+ piccolo-1.29.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
387
+ piccolo-1.29.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
388
+ piccolo-1.29.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
389
+ piccolo-1.29.0.dist-info/RECORD,,
tests/columns/m2m/base.py CHANGED
@@ -11,7 +11,7 @@ from piccolo.columns.m2m import M2M
11
11
  from piccolo.engine.finder import engine_finder
12
12
  from piccolo.schema import SchemaManager
13
13
  from piccolo.table import Table, create_db_tables_sync, drop_db_tables_sync
14
- from tests.base import engine_is, engines_skip
14
+ from tests.base import engines_skip
15
15
 
16
16
  engine = engine_finder()
17
17
 
@@ -51,54 +51,25 @@ class M2MBase:
51
51
 
52
52
  create_db_tables_sync(*self.all_tables, if_not_exists=True)
53
53
 
54
- if engine_is("cockroach"):
55
- bands = (
56
- Band.insert(
57
- Band(name="Pythonistas"),
58
- Band(name="Rustaceans"),
59
- Band(name="C-Sharps"),
60
- )
61
- .returning(Band.id)
62
- .run_sync()
63
- )
64
-
65
- genres = (
66
- Genre.insert(
67
- Genre(name="Rock"),
68
- Genre(name="Folk"),
69
- Genre(name="Classical"),
70
- )
71
- .returning(Genre.id)
72
- .run_sync()
73
- )
74
-
75
- GenreToBand.insert(
76
- GenreToBand(band=bands[0]["id"], genre=genres[0]["id"]),
77
- GenreToBand(band=bands[0]["id"], genre=genres[1]["id"]),
78
- GenreToBand(band=bands[1]["id"], genre=genres[1]["id"]),
79
- GenreToBand(band=bands[2]["id"], genre=genres[0]["id"]),
80
- GenreToBand(band=bands[2]["id"], genre=genres[2]["id"]),
81
- ).run_sync()
82
- else:
83
- Band.insert(
84
- Band(name="Pythonistas"),
85
- Band(name="Rustaceans"),
86
- Band(name="C-Sharps"),
87
- ).run_sync()
54
+ bands = Band.insert(
55
+ Band(name="Pythonistas"),
56
+ Band(name="Rustaceans"),
57
+ Band(name="C-Sharps"),
58
+ ).run_sync()
88
59
 
89
- Genre.insert(
90
- Genre(name="Rock"),
91
- Genre(name="Folk"),
92
- Genre(name="Classical"),
93
- ).run_sync()
60
+ genres = Genre.insert(
61
+ Genre(name="Rock"),
62
+ Genre(name="Folk"),
63
+ Genre(name="Classical"),
64
+ ).run_sync()
94
65
 
95
- GenreToBand.insert(
96
- GenreToBand(band=1, genre=1),
97
- GenreToBand(band=1, genre=2),
98
- GenreToBand(band=2, genre=2),
99
- GenreToBand(band=3, genre=1),
100
- GenreToBand(band=3, genre=3),
101
- ).run_sync()
66
+ GenreToBand.insert(
67
+ GenreToBand(band=bands[0]["id"], genre=genres[0]["id"]),
68
+ GenreToBand(band=bands[0]["id"], genre=genres[1]["id"]),
69
+ GenreToBand(band=bands[1]["id"], genre=genres[1]["id"]),
70
+ GenreToBand(band=bands[2]["id"], genre=genres[0]["id"]),
71
+ GenreToBand(band=bands[2]["id"], genre=genres[2]["id"]),
72
+ ).run_sync()
102
73
 
103
74
  def tearDown(self):
104
75
  drop_db_tables_sync(*self.all_tables)
@@ -1,13 +1,13 @@
1
1
  from unittest import TestCase
2
2
  from unittest.mock import MagicMock, patch
3
3
 
4
- from piccolo.columns import Secret
5
4
  from piccolo.columns.column_types import (
6
5
  JSON,
7
6
  JSONB,
8
7
  Array,
9
8
  Email,
10
9
  ForeignKey,
10
+ Secret,
11
11
  Varchar,
12
12
  )
13
13
  from piccolo.table import TABLENAME_WARNING, Table
@@ -99,14 +99,18 @@ class TestMetaClass(TestCase):
99
99
 
100
100
  def test_secret_columns(self):
101
101
  """
102
- Make sure TableMeta.secret_columns are setup correctly.
102
+ Make sure TableMeta.secret_columns are setup correctly with the
103
+ ``secret=True`` argument and ``Secret`` column type.
103
104
  """
104
105
 
105
106
  class Classified(Table):
106
107
  top_secret = Secret()
108
+ confidential = Varchar(secret=True)
109
+ public = Varchar()
107
110
 
108
111
  self.assertEqual(
109
- Classified._meta.secret_columns, [Classified.top_secret]
112
+ Classified._meta.secret_columns,
113
+ [Classified.top_secret, Classified.confidential],
110
114
  )
111
115
 
112
116
  def test_json_columns(self):
tests/table/test_str.py CHANGED
@@ -1,30 +1,20 @@
1
1
  from unittest import TestCase
2
2
 
3
- from tests.base import engine_is
4
- from tests.example_apps.music.tables import Manager
3
+ from piccolo.apps.playground.commands.run import Genre, Manager
5
4
 
6
5
 
7
6
  class TestTableStr(TestCase):
8
- def test_str(self):
9
- if engine_is("cockroach"):
10
- self.assertEqual(
11
- Manager._table_str(),
12
- (
13
- "class Manager(Table, tablename='manager'):\n"
14
- " id = Serial(null=False, primary_key=True, unique=False, index=False, index_method=IndexMethod.btree, choices=None, db_column_name='id', secret=False)\n" # noqa: E501
15
- " name = Varchar(length=50, default='', null=False, primary_key=False, unique=False, index=False, index_method=IndexMethod.btree, choices=None, db_column_name=None, secret=False)\n" # noqa: E501
16
- ),
17
- )
18
- else:
19
- self.assertEqual(
20
- Manager._table_str(),
21
- (
22
- "class Manager(Table, tablename='manager'):\n"
23
- " id = Serial(null=False, primary_key=True, unique=False, index=False, index_method=IndexMethod.btree, choices=None, db_column_name='id', secret=False)\n" # noqa: E501
24
- " name = Varchar(length=50, default='', null=False, primary_key=False, unique=False, index=False, index_method=IndexMethod.btree, choices=None, db_column_name=None, secret=False)\n" # noqa: E501
25
- ),
26
- )
7
+ def test_all_attributes(self):
8
+ self.assertEqual(
9
+ Manager._table_str(),
10
+ (
11
+ "class Manager(Table, tablename='manager'):\n"
12
+ " id = Serial(null=False, primary_key=True, unique=False, index=False, index_method=IndexMethod.btree, choices=None, db_column_name='id', secret=False)\n" # noqa: E501
13
+ " name = Varchar(length=50, default='', null=False, primary_key=False, unique=False, index=False, index_method=IndexMethod.btree, choices=None, db_column_name=None, secret=False)\n" # noqa: E501
14
+ ),
15
+ )
27
16
 
17
+ def test_abbreviated(self):
28
18
  self.assertEqual(
29
19
  Manager._table_str(abbreviated=True),
30
20
  (
@@ -34,5 +24,23 @@ class TestTableStr(TestCase):
34
24
  ),
35
25
  )
36
26
 
37
- # We should also be able to print it directly.
27
+ def test_m2m(self):
28
+ """
29
+ Make sure M2M relationships appear in the Table string.
30
+ """
31
+
32
+ self.assertEqual(
33
+ Genre._table_str(abbreviated=True),
34
+ (
35
+ "class Genre(Table):\n"
36
+ " id = Serial()\n"
37
+ " name = Varchar()\n"
38
+ " bands = M2M(GenreToBand)\n"
39
+ ),
40
+ )
41
+
42
+ def test_print(self):
43
+ """
44
+ Make sure we can print it directly without any errors.
45
+ """
38
46
  print(Manager)
@@ -158,6 +158,7 @@ class TestUpdate(DBTestCase):
158
158
 
159
159
  class MyTable(Table):
160
160
  integer = Integer(null=True)
161
+ other_integer = Integer(null=True, default=5)
161
162
  timestamp = Timestamp(null=True)
162
163
  timestamptz = Timestamptz(null=True)
163
164
  date = Date(null=True)
@@ -295,6 +296,20 @@ TEST_CASES = [
295
296
  querystring=2000 - MyTable.integer,
296
297
  expected=1000,
297
298
  ),
299
+ OperatorTestCase(
300
+ description="Subtract Integer Columns",
301
+ column=MyTable.integer,
302
+ initial=1000,
303
+ querystring=MyTable.integer - MyTable.other_integer,
304
+ expected=995,
305
+ ),
306
+ OperatorTestCase(
307
+ description="Add Integer Columns",
308
+ column=MyTable.integer,
309
+ initial=1000,
310
+ querystring=MyTable.integer + MyTable.other_integer,
311
+ expected=1005,
312
+ ),
298
313
  OperatorTestCase(
299
314
  description="Multiply Integer",
300
315
  column=MyTable.integer,