piccolo 0.109.0__py3-none-any.whl → 0.111.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,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import json
2
4
  import typing as t
3
5
  from datetime import date, datetime, time, timedelta
@@ -5,7 +7,7 @@ from decimal import Decimal
5
7
  from uuid import UUID
6
8
 
7
9
  from piccolo.columns import Array, Column
8
- from piccolo.table import Table
10
+ from piccolo.custom_types import TableInstance
9
11
  from piccolo.testing.random_builder import RandomBuilder
10
12
  from piccolo.utils.sync import run_sync
11
13
 
@@ -27,11 +29,11 @@ class ModelBuilder:
27
29
  @classmethod
28
30
  async def build(
29
31
  cls,
30
- table_class: t.Type[Table],
32
+ table_class: t.Type[TableInstance],
31
33
  defaults: t.Dict[t.Union[Column, str], t.Any] = None,
32
34
  persist: bool = True,
33
35
  minimal: bool = False,
34
- ) -> Table:
36
+ ) -> TableInstance:
35
37
  """
36
38
  Build a ``Table`` instance with random data and save async.
37
39
  If the ``Table`` has any foreign keys, then the related rows are also
@@ -78,11 +80,11 @@ class ModelBuilder:
78
80
  @classmethod
79
81
  def build_sync(
80
82
  cls,
81
- table_class: t.Type[Table],
83
+ table_class: t.Type[TableInstance],
82
84
  defaults: t.Dict[t.Union[Column, str], t.Any] = None,
83
85
  persist: bool = True,
84
86
  minimal: bool = False,
85
- ) -> Table:
87
+ ) -> TableInstance:
86
88
  """
87
89
  A sync wrapper around :meth:`build`.
88
90
  """
@@ -98,11 +100,11 @@ class ModelBuilder:
98
100
  @classmethod
99
101
  async def _build(
100
102
  cls,
101
- table_class: t.Type[Table],
103
+ table_class: t.Type[TableInstance],
102
104
  defaults: t.Dict[t.Union[Column, str], t.Any] = None,
103
105
  minimal: bool = False,
104
106
  persist: bool = True,
105
- ) -> Table:
107
+ ) -> TableInstance:
106
108
  model = table_class(_ignore_missing=True)
107
109
  defaults = {} if not defaults else defaults
108
110
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: piccolo
3
- Version: 0.109.0
3
+ Version: 0.111.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
@@ -144,7 +144,7 @@ Let Piccolo scaffold you an ASGI web app, using Piccolo as the ORM:
144
144
  piccolo asgi new
145
145
  ```
146
146
 
147
- [Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), [BlackSheep](https://www.neoteroi.dev/blacksheep/), [Xpresso](https://xpresso-api.dev/) and [Starlite](https://starlite-api.github.io/starlite/) are currently supported.
147
+ [Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), [BlackSheep](https://www.neoteroi.dev/blacksheep/) and [Litestar](https://litestar.dev/) are currently supported.
148
148
 
149
149
  ## Are you a Django user?
150
150
 
@@ -1,4 +1,4 @@
1
- piccolo/__init__.py,sha256=0KbSmIu_5PgqXUdmc7nIf-QktxNlQI_QsU4eiZclOJ8,24
1
+ piccolo/__init__.py,sha256=FD-yuKtGvNyh8GH2WGzOJQnF7v7XuGXevze_b_yuJ4g,24
2
2
  piccolo/custom_types.py,sha256=7HMQAze-5mieNLfbQ5QgbRQgR2abR7ol0qehv2SqROY,604
3
3
  piccolo/main.py,sha256=2W2EXXEr-EN1PG8s8xHIWCvU7t7kT004fBChK9CZhzo,5024
4
4
  piccolo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -16,14 +16,13 @@ piccolo/apps/app/commands/templates/tables.py.jinja,sha256=revzdrvDDwe78VedBKz0z
16
16
  piccolo/apps/asgi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  piccolo/apps/asgi/piccolo_app.py,sha256=7VUvqQJbB-ScO0A62S6MiJmQL9F5DS-SdlqlDLbAblE,217
18
18
  piccolo/apps/asgi/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- piccolo/apps/asgi/commands/new.py,sha256=yuGOGy6bJtqyBlJeaegajWyUqtuN1A3mPXXQhSU6JfE,4160
19
+ piccolo/apps/asgi/commands/new.py,sha256=P64IwftHM3uHkUObl6tVXmBfsTaFon7im6LEgDFwg10,4100
20
20
  piccolo/apps/asgi/commands/templates/app/README.md.jinja,sha256=As3gNEZt9qcRmTVkjCzNtXJ8r4-3g0fCSe7Q-P39ezI,214
21
21
  piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja,sha256=bgAGe0a9nWk0LAqK3VNDhPcKGqg0z8V-eIX2YmMoZLk,3117
22
22
  piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja,sha256=cCmRVAN8gw6zfHBcLI_NxapwN7LGM5QSXM7K94imDh8,2436
23
+ piccolo/apps/asgi/commands/templates/app/_litestar_app.py.jinja,sha256=e0784WBJSsrAPmDXjVuYfmrUJWLAkQyDxoSDl80ucWQ,2622
23
24
  piccolo/apps/asgi/commands/templates/app/_starlette_app.py.jinja,sha256=YvNUlJuTd4mj-pm3WQKbQq3w3x3VfDb_Wz6aQLUsORo,1271
24
- piccolo/apps/asgi/commands/templates/app/_starlite_app.py.jinja,sha256=RUtYfORvmV5gyKZVvBuR07R9Rf9Ai9fH1c85NYAAtgc,2636
25
- piccolo/apps/asgi/commands/templates/app/_xpresso_app.py.jinja,sha256=D3D3ScpItr0V4InaqBMFwy4BvnSMI2AM_6E6sKmUjJE,2855
26
- piccolo/apps/asgi/commands/templates/app/app.py.jinja,sha256=Hsoq3xsCc3yzNFHXV3zRBxrh42J9W04qyVc7FZ7Bg4Y,387
25
+ piccolo/apps/asgi/commands/templates/app/app.py.jinja,sha256=NnpkkIFswR4SUsOrhB6x2rNrDfR_QOaGewRv_mP5zgQ,314
27
26
  piccolo/apps/asgi/commands/templates/app/conftest.py.jinja,sha256=ZG1pRVMv3LhIfOsO3_08c_fF3EV4_EApuDHiIFFPJdk,497
28
27
  piccolo/apps/asgi/commands/templates/app/main.py.jinja,sha256=azwXyWZGkrIbZv5bZF_4Tvbly7AXkw5yFWGCHYImGeo,421
29
28
  piccolo/apps/asgi/commands/templates/app/piccolo_conf.py.jinja,sha256=f9Nb08_yipi0_mDUYrUvVoGCz7MRRS5QjCdUGBHN760,379
@@ -31,15 +30,14 @@ piccolo/apps/asgi/commands/templates/app/piccolo_conf_test.py.jinja,sha256=ZB32I
31
30
  piccolo/apps/asgi/commands/templates/app/requirements.txt.jinja,sha256=gR_AvX3YH0bsjkAY28eXNy_D1YuJAvEnT1tGtVzMnnY,152
32
31
  piccolo/apps/asgi/commands/templates/app/home/__init__.py.jinja,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
32
  piccolo/apps/asgi/commands/templates/app/home/_blacksheep_endpoints.py.jinja,sha256=Rri_xzDkl87G5ME74qTxY25cwKIKufuzgkRsy__mNts,510
33
+ piccolo/apps/asgi/commands/templates/app/home/_litestar_endpoints.py.jinja,sha256=mk0LTygP-HOaiWYO4SDnJ3C-WZxLxRFYxUrIYwatAyc,531
34
34
  piccolo/apps/asgi/commands/templates/app/home/_starlette_endpoints.py.jinja,sha256=KEjNEUKiZNBIWYAt9EgPHe4yCbkKLtlhaCBce9YI-RQ,498
35
- piccolo/apps/asgi/commands/templates/app/home/_starlite_endpoints.py.jinja,sha256=D8dT3-hSGYcmCdxIRi-F-YJVa3EQdcbuDo6NZZjbySI,530
36
- piccolo/apps/asgi/commands/templates/app/home/_xpresso_endpoints.py.jinja,sha256=JO9SCIL2jDAkoop8zPhdTeUt_oP-Zent2KqIt6NAPy4,402
37
- piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja,sha256=dgpHlA8mjmYCnNs5RiToUj3S3u6mZObOVC1VU-jbRCc,351
35
+ piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja,sha256=OeRMBHjjxVyGIXAuSLQzl6qsWOmD7-pZcToA3V5gWx4,272
38
36
  piccolo/apps/asgi/commands/templates/app/home/piccolo_app.py.jinja,sha256=4gETiW9ukTNsomeJOvrRkqPbToZ_FU0b3LsNIaEYyP8,505
39
37
  piccolo/apps/asgi/commands/templates/app/home/tables.py.jinja,sha256=wk34RAsuoFn5iJ4OHlQzUqgatq6QB2G9tFE0BYkaers,197
40
38
  piccolo/apps/asgi/commands/templates/app/home/piccolo_migrations/README.md,sha256=ji6UOtHvzHX-eS_qhhKTN36ZXNZ7QwtjwjdE4Qgm35A,59
41
39
  piccolo/apps/asgi/commands/templates/app/home/templates/base.html.jinja_raw,sha256=3RqiNuyAap_P-xNK3uhNaQQ6rC365VzPmRqmmXSLO8o,451
42
- piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw,sha256=G7M_jt0ueeW0W-zkt35pFL55tpNtHOaeIuZKHBaD61A,2453
40
+ piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw,sha256=u_oXpGzR3o_eCufFPRpjD6iL1SdiixvrmOOeWp7WF10,2278
43
41
  piccolo/apps/asgi/commands/templates/app/static/favicon.ico,sha256=IvcgeJHObd9kj2mNIXkJdXYxMU8OaOymyYQWnWfbtHo,7406
44
42
  piccolo/apps/asgi/commands/templates/app/static/main.css,sha256=vudarPLglQ6NOgJiNeU2x0yQl0DiWScqb09QZv2wAzM,1056
45
43
  piccolo/apps/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -74,7 +72,7 @@ piccolo/apps/migrations/commands/templates/migration.py.jinja,sha256=wMC8RTIcQj3
74
72
  piccolo/apps/playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
73
  piccolo/apps/playground/piccolo_app.py,sha256=zs6nGxt-lgUF8nEwI0uDTNZDKQqjZaNDH8le5RqrMNE,222
76
74
  piccolo/apps/playground/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- piccolo/apps/playground/commands/run.py,sha256=ee0pbj_panKndmQjyzy_1P1Gp850IITdEcm2nFpk8Nk,6949
75
+ piccolo/apps/playground/commands/run.py,sha256=PaY3ls4C0j0TnVSTY85abv6e2SuTsII0H33HlkXlZzc,7350
78
76
  piccolo/apps/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
77
  piccolo/apps/project/piccolo_app.py,sha256=mT3O0m3QcCfS0oOr3jt0QZ9TX6gUavGPjJeNn2C_fdM,220
80
78
  piccolo/apps/project/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -113,7 +111,7 @@ piccolo/apps/user/piccolo_migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
113
111
  piccolo/columns/__init__.py,sha256=OYhO_n9anMiU9nL-K6ATq9FhAtm8RyMpqYQ7fTVbhxI,1120
114
112
  piccolo/columns/base.py,sha256=lD3fzhpHCVlfieME2q9gCiHdpJqKNo2Te67HrjUX2c0,31355
115
113
  piccolo/columns/choices.py,sha256=-HNQuk9vMmVZIPZ5PMeXGTfr23o4nzKPSAkvcG1k0y8,723
116
- piccolo/columns/column_types.py,sha256=P1zPKkDoXWfI-Qsc7CiAcYF6jB-SbqRPPe7kK5EVsq0,77278
114
+ piccolo/columns/column_types.py,sha256=X0nTvlc8epfcu8Q8uuIBlnHv_R_GTJ58dehC343vNgA,77278
117
115
  piccolo/columns/combination.py,sha256=vMXC2dfY7pvnCFhsT71XFVyb4gdQzfRsCMaiduu04Ss,6900
118
116
  piccolo/columns/indexes.py,sha256=NfNok3v_791jgDlN28KmhP9ZCjl6031BXmjxV3ovXJk,372
119
117
  piccolo/columns/m2m.py,sha256=C7IKMg7ik2yE3143Gwdbx3YNB3VrZbltJAlX0XxQwAI,14067
@@ -143,7 +141,7 @@ piccolo/engine/postgres.py,sha256=UvGihQeUgg2MniTN5mABlSMPkBgtQQSmx9QE32uv9SA,18
143
141
  piccolo/engine/sqlite.py,sha256=io3fBdXxXjOoSzLgP-HYGKgTDgHIRpmFa7mU6ifbqn8,21915
144
142
  piccolo/query/__init__.py,sha256=WkG78nTz4Ww3rE9Pu5tZI130JMVfGnwyghhu97XFk0w,617
145
143
  piccolo/query/base.py,sha256=BsrzbeuJo7k-KJn4YlRUibxITw_J7xslbgJg0eVFB8s,15124
146
- piccolo/query/mixins.py,sha256=_JcvWZ-IQCvIyLsW40VGDItSOH48BRcm-Zd-r1Un71Q,14442
144
+ piccolo/query/mixins.py,sha256=N6HAN_A4kd-PC07q3OIzwrkRy3ZwGMtB2xLueYefBSM,21649
147
145
  piccolo/query/proxy.py,sha256=Hg5S6tp1EiKD899eYdDKHscFYucHdKtL3YC2GTcL2Jk,1833
148
146
  piccolo/query/methods/__init__.py,sha256=_PfGUdOd6AsKq1sqXeZUHhESHE-e1cNpwFr8Lyz7QoY,421
149
147
  piccolo/query/methods/alter.py,sha256=gyx4kVF4EiN4sSFjIqcKykxcKB808j1ioa2lKrLdP4Y,14935
@@ -154,15 +152,15 @@ piccolo/query/methods/delete.py,sha256=JkDMkee3LVKtZdAS2AQnNFkDbxcus3xq7kbDAu6Fk
154
152
  piccolo/query/methods/drop_index.py,sha256=SOX5wfm-Tbb5TrN6kaLRVHUWdEhyrmCQwF33JfWdtwE,1043
155
153
  piccolo/query/methods/exists.py,sha256=LAeWpGKEMYZJeNGEcxbucxWDxAjn84jduNz2ZjzukPc,1181
156
154
  piccolo/query/methods/indexes.py,sha256=_Io7vy_yIjPYHIw1ILGhAiWQAEOIe-Lwn3mEELw8mOc,1083
157
- piccolo/query/methods/insert.py,sha256=qmwvPysnrANjubF-F41TtBYKVdy2dHfw_lgD3C_T9Kw,2998
158
- piccolo/query/methods/objects.py,sha256=CAsZjTzOtD6elMqc2nIK_Ra1GKu7sxHBgcDRh9NAes8,11556
155
+ piccolo/query/methods/insert.py,sha256=BwIJb1g0PFqMws060Wn0uSAK1ybLbkK9mKUfOOmzroc,4716
156
+ piccolo/query/methods/objects.py,sha256=i71GHPJZJcRpgM6e69Vk7vVcCuAFokOsMnR5EXbZq1w,11673
159
157
  piccolo/query/methods/raw.py,sha256=VhYpCB52mZk4zqFTsqK5CHKTDGskUjISXTBV7UjohmA,600
160
158
  piccolo/query/methods/refresh.py,sha256=P1Eo_HYU_L7kcGM_cvDDgyLi1boCXY7Pc4tv_eDAzvc,2769
161
- piccolo/query/methods/select.py,sha256=xlHjDnTMsccpQKM6jdDJiVv6rv8o0-R8t4FCG__3-gA,24997
159
+ piccolo/query/methods/select.py,sha256=6LVVNJEIooB0cQItI77rxwURsA88ifTOm1dj-wFlHRY,25406
162
160
  piccolo/query/methods/table_exists.py,sha256=rPY20QNdJI9TvKjGyTPVvGGEuD3bDnQim8K1ZurthmU,1211
163
161
  piccolo/query/methods/update.py,sha256=0hURc7PQU9NX7QQFJ1XgFJvw3nXYIrWUjE-D_7W5JV4,3625
164
162
  piccolo/testing/__init__.py,sha256=pRFSqRInfx95AakOq54atmvqoB-ue073q2aR8u8zR40,83
165
- piccolo/testing/model_builder.py,sha256=6-s4rzLKj6f0NldrV2L2JQlbqC7mJMn-5mNoAtUwU34,5635
163
+ piccolo/testing/model_builder.py,sha256=_tss3L-n-hwIaygNJ3dVmWvZxXE035h2QdDaLYJhH7c,5734
166
164
  piccolo/testing/random_builder.py,sha256=o3Ebzak1AG_3nG1iIYN2ZNn5NKQTRECha4ZEubAl9yQ,2005
167
165
  piccolo/utils/__init__.py,sha256=SDFFraauI9Op8dCRkreQv1dwUcab8Mi1eC-n0EwlTy8,36
168
166
  piccolo/utils/dictionary.py,sha256=8vRPxgaXadDVhqihP1UxL7nUBgM6Gpe_Eu3xJq7zzGM,1886
@@ -305,7 +303,7 @@ tests/table/test_exists.py,sha256=AHvhodkRof7PVd4IDdGQ2nyOj_1Cag1Rpg1H84s4jU0,28
305
303
  tests/table/test_from_dict.py,sha256=I4PMxuzgkgi3-adaw9Gr3u5tQHexc31Vrq7RSPcPcJs,840
306
304
  tests/table/test_indexes.py,sha256=GdlPfLmvM0s2fe-4-2XSqQLYYjwsBu5dzf6o7ZvlPj4,1990
307
305
  tests/table/test_inheritance.py,sha256=s5JIo8hZN7xqOPlZ9EDkkNLo5_kWirsfCJAqaXSHn88,3034
308
- tests/table/test_insert.py,sha256=8G7TvgZIKqKNDJR0-Ck5gTtzQa0c89OeFNaWf9wPpBk,2274
306
+ tests/table/test_insert.py,sha256=LeYNvApZ2T-PZ9fseINLZ6hnrY5F1Axe3QHqidwzbAQ,13668
309
307
  tests/table/test_join.py,sha256=tk2r5OUaay9-4U37aj2-qul1XybchBG3xr-k7K_IQh0,14705
310
308
  tests/table/test_join_on.py,sha256=NhJRg_7_YQ0o2ox5mF330ZaIvmtq09Xl2lfDTwKtUng,2719
311
309
  tests/table/test_metaclass.py,sha256=liJuKArpco1qb3lshSQTwRsqXXZZNgzmFoMDP9r2uHw,2637
@@ -315,7 +313,7 @@ tests/table/test_raw.py,sha256=AxT6qB0bEjVbOz6lmGQ9_IiDuEoVmh5c72gzyiatBwo,1683
315
313
  tests/table/test_ref.py,sha256=eYNRnYHzNMXuMbV3B1ca5EidpIg4500q6hr1ccuVaso,269
316
314
  tests/table/test_refresh.py,sha256=tZktBoUQth3S2_vou5NcKiwOJDFrhF6CIuC7YZ2janw,2694
317
315
  tests/table/test_repr.py,sha256=dKdM0HRygvqjmSPz-l95SJQXQ-O18MHUGrcIzzYKrsQ,411
318
- tests/table/test_select.py,sha256=Dcw-4hbLDv2OvQJ12qFibKJ5CmslUdfQNWds_VuPFhc,34824
316
+ tests/table/test_select.py,sha256=sjSzJfHaYG8Zrg2jXbp7eNprqXilMzDHDxpM3k0BCFg,39678
319
317
  tests/table/test_str.py,sha256=eztWNULcjARR1fr9X5n4tojhDNgDfatVyNHwuYrzHAo,1731
320
318
  tests/table/test_table_exists.py,sha256=9Qqwbg6Q3OfasSH4FUqD3z99ExvJqpHSu0pYu7aa998,375
321
319
  tests/table/test_update.py,sha256=ZEkIDgQ9PHcAn58dlN4WIHRJm0RENi6AVDsmgLX9Qlw,20342
@@ -342,9 +340,9 @@ tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5
342
340
  tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
343
341
  tests/utils/test_table_reflection.py,sha256=SIzuat-IpcVj1GCFyOWKShI8YkhdOPPFH7qVrvfyPNE,3794
344
342
  tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
345
- piccolo-0.109.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
346
- piccolo-0.109.0.dist-info/METADATA,sha256=7uqM1YtjsbxK6bGP66e8Yf1vbadKg2Edtvsqoz5j0SU,5169
347
- piccolo-0.109.0.dist-info/WHEEL,sha256=00yskusixUoUt5ob_CiUp6LsnN5lqzTJpoqOFg_FVIc,92
348
- piccolo-0.109.0.dist-info/entry_points.txt,sha256=zYhu-YNtMlh2N_8wptCS8YWKOgc81UPL3Ji5gly8ouc,47
349
- piccolo-0.109.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
350
- piccolo-0.109.0.dist-info/RECORD,,
343
+ piccolo-0.111.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
344
+ piccolo-0.111.0.dist-info/METADATA,sha256=mtWcL-RTGmGGTTQuSJT8wm_KPoQerNk1MHE_8It-4Mo,5113
345
+ piccolo-0.111.0.dist-info/WHEEL,sha256=00yskusixUoUt5ob_CiUp6LsnN5lqzTJpoqOFg_FVIc,92
346
+ piccolo-0.111.0.dist-info/entry_points.txt,sha256=zYhu-YNtMlh2N_8wptCS8YWKOgc81UPL3Ji5gly8ouc,47
347
+ piccolo-0.111.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
348
+ piccolo-0.111.0.dist-info/RECORD,,
@@ -1,8 +1,22 @@
1
+ import sqlite3
2
+ from unittest import TestCase
3
+
1
4
  import pytest
2
5
 
3
- from tests.base import DBTestCase, engine_version_lt, is_running_sqlite
6
+ from piccolo.columns import Integer, Varchar
7
+ from piccolo.query.methods.insert import OnConflictAction
8
+ from piccolo.table import Table
9
+ from piccolo.utils.lazy_loader import LazyLoader
10
+ from tests.base import (
11
+ DBTestCase,
12
+ engine_version_lt,
13
+ engines_only,
14
+ is_running_sqlite,
15
+ )
4
16
  from tests.example_apps.music.tables import Band, Manager
5
17
 
18
+ asyncpg = LazyLoader("asyncpg", globals(), "asyncpg")
19
+
6
20
 
7
21
  class TestInsert(DBTestCase):
8
22
  def test_insert(self):
@@ -76,3 +90,385 @@ class TestInsert(DBTestCase):
76
90
  )
77
91
 
78
92
  self.assertListEqual(response, [{"manager_name": "Maz"}])
93
+
94
+
95
+ @pytest.mark.skipif(
96
+ is_running_sqlite() and engine_version_lt(3.24),
97
+ reason="SQLite version not supported",
98
+ )
99
+ class TestOnConflict(TestCase):
100
+ class Band(Table):
101
+ name = Varchar(unique=True)
102
+ popularity = Integer()
103
+
104
+ def setUp(self) -> None:
105
+ Band = self.Band
106
+ Band.create_table().run_sync()
107
+ self.band = Band({Band.name: "Pythonistas", Band.popularity: 1000})
108
+ self.band.save().run_sync()
109
+
110
+ def tearDown(self) -> None:
111
+ Band = self.Band
112
+ Band.alter().drop_table().run_sync()
113
+
114
+ def test_do_update(self):
115
+ """
116
+ Make sure that `DO UPDATE` works.
117
+ """
118
+ Band = self.Band
119
+
120
+ new_popularity = self.band.popularity + 1000
121
+
122
+ Band.insert(
123
+ Band(name=self.band.name, popularity=new_popularity)
124
+ ).on_conflict(
125
+ target=Band.name,
126
+ action="DO UPDATE",
127
+ values=[Band.popularity],
128
+ ).run_sync()
129
+
130
+ self.assertListEqual(
131
+ Band.select().run_sync(),
132
+ [
133
+ {
134
+ "id": self.band.id,
135
+ "name": self.band.name,
136
+ "popularity": new_popularity, # changed
137
+ }
138
+ ],
139
+ )
140
+
141
+ def test_do_update_tuple_values(self):
142
+ """
143
+ Make sure we can use tuples in ``values``.
144
+ """
145
+ Band = self.Band
146
+
147
+ new_popularity = self.band.popularity + 1000
148
+ new_name = "Rustaceans"
149
+
150
+ Band.insert(
151
+ Band(
152
+ id=self.band.id,
153
+ name=new_name,
154
+ popularity=new_popularity,
155
+ )
156
+ ).on_conflict(
157
+ action="DO UPDATE",
158
+ target=Band.id,
159
+ values=[
160
+ (Band.name, new_name),
161
+ (Band.popularity, new_popularity + 2000),
162
+ ],
163
+ ).run_sync()
164
+
165
+ self.assertListEqual(
166
+ Band.select().run_sync(),
167
+ [
168
+ {
169
+ "id": self.band.id,
170
+ "name": new_name,
171
+ "popularity": new_popularity + 2000,
172
+ }
173
+ ],
174
+ )
175
+
176
+ def test_do_update_no_values(self):
177
+ """
178
+ Make sure that `DO UPDATE` with no `values` raises an exception.
179
+ """
180
+ Band = self.Band
181
+
182
+ new_popularity = self.band.popularity + 1000
183
+
184
+ with self.assertRaises(ValueError) as manager:
185
+ Band.insert(
186
+ Band(name=self.band.name, popularity=new_popularity)
187
+ ).on_conflict(
188
+ target=Band.name,
189
+ action="DO UPDATE",
190
+ ).run_sync()
191
+
192
+ self.assertEqual(
193
+ manager.exception.__str__(),
194
+ "No values specified for `on conflict`",
195
+ )
196
+
197
+ @engines_only("postgres", "cockroach")
198
+ def test_target_tuple(self):
199
+ """
200
+ Make sure that a composite unique constraint can be used as a target.
201
+
202
+ We only run it on Postgres and Cockroach because we use ALTER TABLE
203
+ to add a contraint, which SQLite doesn't support.
204
+ """
205
+ Band = self.Band
206
+
207
+ # Add a composite unique constraint:
208
+ Band.raw(
209
+ "ALTER TABLE band ADD CONSTRAINT id_name_unique UNIQUE (id, name)"
210
+ ).run_sync()
211
+
212
+ Band.insert(
213
+ Band(
214
+ id=self.band.id,
215
+ name=self.band.name,
216
+ popularity=self.band.popularity,
217
+ )
218
+ ).on_conflict(
219
+ target=(Band.id, Band.name),
220
+ action="DO NOTHING",
221
+ ).run_sync()
222
+
223
+ @engines_only("postgres", "cockroach")
224
+ def test_target_string(self):
225
+ """
226
+ Make sure we can explicitly specify the name of target constraint using
227
+ a string.
228
+
229
+ We just test this on Postgres for now, as we have to get the constraint
230
+ name from the database.
231
+ """
232
+ Band = self.Band
233
+
234
+ constraint_name = Band.raw(
235
+ """
236
+ SELECT constraint_name
237
+ FROM information_schema.constraint_column_usage
238
+ WHERE column_name = 'name'
239
+ AND table_name = 'band';
240
+ """
241
+ ).run_sync()[0]["constraint_name"]
242
+
243
+ query = Band.insert(Band(name=self.band.name)).on_conflict(
244
+ target=constraint_name,
245
+ action="DO NOTHING",
246
+ )
247
+ self.assertIn(f'ON CONSTRAINT "{constraint_name}"', query.__str__())
248
+ query.run_sync()
249
+
250
+ def test_violate_non_target(self):
251
+ """
252
+ Make sure that if we specify a target constraint, but violate a
253
+ different constraint, then we still get the error.
254
+ """
255
+ Band = self.Band
256
+
257
+ new_popularity = self.band.popularity + 1000
258
+
259
+ with self.assertRaises(Exception) as manager:
260
+ Band.insert(
261
+ Band(name=self.band.name, popularity=new_popularity)
262
+ ).on_conflict(
263
+ target=Band.id, # Target the primary key instead.
264
+ action="DO UPDATE",
265
+ values=[Band.popularity],
266
+ ).run_sync()
267
+
268
+ if self.Band._meta.db.engine_type in ("postgres", "cockroach"):
269
+ self.assertIsInstance(
270
+ manager.exception, asyncpg.exceptions.UniqueViolationError
271
+ )
272
+ elif self.Band._meta.db.engine_type == "sqlite":
273
+ self.assertIsInstance(manager.exception, sqlite3.IntegrityError)
274
+
275
+ def test_where(self):
276
+ """
277
+ Make sure we can pass in a `where` argument.
278
+ """
279
+ Band = self.Band
280
+
281
+ new_popularity = self.band.popularity + 1000
282
+
283
+ query = Band.insert(
284
+ Band(name=self.band.name, popularity=new_popularity)
285
+ ).on_conflict(
286
+ target=Band.name,
287
+ action="DO UPDATE",
288
+ values=[Band.popularity],
289
+ where=Band.popularity < self.band.popularity,
290
+ )
291
+
292
+ self.assertIn(
293
+ f'WHERE "band"."popularity" < {self.band.popularity}',
294
+ query.__str__(),
295
+ )
296
+
297
+ query.run_sync()
298
+
299
+ def test_do_nothing_where(self):
300
+ """
301
+ Make sure an error is raised if `where` is used with `DO NOTHING`.
302
+ """
303
+ Band = self.Band
304
+
305
+ with self.assertRaises(ValueError) as manager:
306
+ Band.insert(Band()).on_conflict(
307
+ action="DO NOTHING",
308
+ where=Band.popularity < self.band.popularity,
309
+ )
310
+
311
+ self.assertEqual(
312
+ manager.exception.__str__(),
313
+ "The `where` option can only be used with DO NOTHING.",
314
+ )
315
+
316
+ def test_do_nothing(self):
317
+ """
318
+ Make sure that `DO NOTHING` works.
319
+ """
320
+ Band = self.Band
321
+
322
+ new_popularity = self.band.popularity + 1000
323
+
324
+ Band.insert(
325
+ Band(name="Pythonistas", popularity=new_popularity)
326
+ ).on_conflict(action="DO NOTHING").run_sync()
327
+
328
+ self.assertListEqual(
329
+ Band.select().run_sync(),
330
+ [
331
+ {
332
+ "id": self.band.id,
333
+ "name": self.band.name,
334
+ "popularity": self.band.popularity,
335
+ }
336
+ ],
337
+ )
338
+
339
+ @engines_only("sqlite")
340
+ def test_multiple_do_update(self):
341
+ """
342
+ Make sure multiple `ON CONFLICT` clauses work for SQLite.
343
+ """
344
+ Band = self.Band
345
+
346
+ new_popularity = self.band.popularity + 1000
347
+
348
+ # Conflicting with name - should update.
349
+ Band.insert(
350
+ Band(name="Pythonistas", popularity=new_popularity)
351
+ ).on_conflict(action="DO NOTHING", target=Band.id).on_conflict(
352
+ action="DO UPDATE", target=Band.name, values=[Band.popularity]
353
+ ).run_sync()
354
+
355
+ self.assertListEqual(
356
+ Band.select().run_sync(),
357
+ [
358
+ {
359
+ "id": self.band.id,
360
+ "name": self.band.name,
361
+ "popularity": new_popularity, # changed
362
+ }
363
+ ],
364
+ )
365
+
366
+ @engines_only("sqlite")
367
+ def test_multiple_do_nothing(self):
368
+ """
369
+ Make sure multiple `ON CONFLICT` clauses work for SQLite.
370
+ """
371
+ Band = self.Band
372
+
373
+ new_popularity = self.band.popularity + 1000
374
+
375
+ # Conflicting with ID - should be ignored.
376
+ Band.insert(
377
+ Band(
378
+ id=self.band.id,
379
+ name="Pythonistas",
380
+ popularity=new_popularity,
381
+ )
382
+ ).on_conflict(action="DO NOTHING", target=Band.id).on_conflict(
383
+ action="DO UPDATE",
384
+ target=Band.name,
385
+ values=[Band.popularity],
386
+ ).run_sync()
387
+
388
+ self.assertListEqual(
389
+ Band.select().run_sync(),
390
+ [
391
+ {
392
+ "id": self.band.id,
393
+ "name": self.band.name,
394
+ "popularity": self.band.popularity,
395
+ }
396
+ ],
397
+ )
398
+
399
+ @engines_only("postgres", "cockroach")
400
+ def test_mutiple_error(self):
401
+ """
402
+ Postgres and Cockroach don't support multiple `ON CONFLICT` clauses.
403
+ """
404
+ with self.assertRaises(NotImplementedError) as manager:
405
+ Band = self.Band
406
+
407
+ Band.insert(Band()).on_conflict(action="DO NOTHING").on_conflict(
408
+ action="DO UPDATE",
409
+ ).run_sync()
410
+
411
+ assert manager.exception.__str__() == (
412
+ "Postgres and Cockroach only support a single ON CONFLICT clause."
413
+ )
414
+
415
+ def test_all_columns(self):
416
+ """
417
+ We can use ``all_columns`` instead of specifying the ``values``
418
+ manually.
419
+ """
420
+ Band = self.Band
421
+
422
+ new_popularity = self.band.popularity + 1000
423
+ new_name = "Rustaceans"
424
+
425
+ # Conflicting with ID - should be ignored.
426
+ q = Band.insert(
427
+ Band(
428
+ id=self.band.id,
429
+ name=new_name,
430
+ popularity=new_popularity,
431
+ )
432
+ ).on_conflict(
433
+ action="DO UPDATE",
434
+ target=Band.id,
435
+ values=Band.all_columns(),
436
+ )
437
+ q.run_sync()
438
+
439
+ self.assertListEqual(
440
+ Band.select().run_sync(),
441
+ [
442
+ {
443
+ "id": self.band.id,
444
+ "name": new_name,
445
+ "popularity": new_popularity,
446
+ }
447
+ ],
448
+ )
449
+
450
+ def test_enum(self):
451
+ """
452
+ A string literal can be passed in, or an enum, to determine the action.
453
+ Make sure that the enum works.
454
+ """
455
+ Band = self.Band
456
+
457
+ Band.insert(
458
+ Band(
459
+ id=self.band.id,
460
+ name=self.band.name,
461
+ popularity=self.band.popularity,
462
+ )
463
+ ).on_conflict(action=OnConflictAction.do_nothing).run_sync()
464
+
465
+ self.assertListEqual(
466
+ Band.select().run_sync(),
467
+ [
468
+ {
469
+ "id": self.band.id,
470
+ "name": self.band.name,
471
+ "popularity": self.band.popularity,
472
+ }
473
+ ],
474
+ )