piccolo 1.17.0__py3-none-any.whl → 1.18.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 CHANGED
@@ -1 +1 @@
1
- __VERSION__ = "1.17.0"
1
+ __VERSION__ = "1.18.0"
@@ -15,7 +15,7 @@ ROUTER_DEPENDENCIES = {
15
15
  "fastapi": ["fastapi>=0.112.1"],
16
16
  "blacksheep": ["blacksheep"],
17
17
  "litestar": ["litestar"],
18
- "esmerald": ["esmerald"],
18
+ "esmerald": ["esmerald==3.3.0"],
19
19
  "lilya": ["lilya"],
20
20
  }
21
21
  ROUTERS = list(ROUTER_DEPENDENCIES.keys())
@@ -40,10 +40,17 @@ def compare_dicts(
40
40
 
41
41
  for key, value in dict_1.items():
42
42
  dict_2_value = dict_2.get(key, ...)
43
+
43
44
  if (
44
- dict_2_value is not ...
45
- and dict_2_value != value
46
- or dict_2_value is ...
45
+ # If the value is `...` then it means no value was found.
46
+ (dict_2_value is ...)
47
+ # We have to compare the types, because if we just use equality
48
+ # then 1.0 == 1 is True.
49
+ # See this issue:
50
+ # https://github.com/piccolo-orm/piccolo/issues/1071
51
+ or (type(value) is not type(dict_2_value))
52
+ # Finally compare the actual values.
53
+ or (dict_2_value != value)
47
54
  ):
48
55
  output[key] = value
49
56
 
@@ -27,6 +27,7 @@ from piccolo.utils.sync import run_sync
27
27
 
28
28
  if t.TYPE_CHECKING: # pragma: no cover
29
29
  from piccolo.columns import Column
30
+ from piccolo.table import Table
30
31
 
31
32
 
32
33
  ###############################################################################
@@ -173,6 +174,61 @@ class Create(t.Generic[TableInstance]):
173
174
  return run_sync(self.run(*args, **kwargs))
174
175
 
175
176
 
177
+ class UpdateSelf:
178
+
179
+ def __init__(
180
+ self,
181
+ row: Table,
182
+ values: t.Dict[t.Union[Column, str], t.Any],
183
+ ):
184
+ self.row = row
185
+ self.values = values
186
+
187
+ async def run(
188
+ self,
189
+ node: t.Optional[str] = None,
190
+ in_pool: bool = True,
191
+ ) -> None:
192
+ if not self.row._exists_in_db:
193
+ raise ValueError("This row doesn't exist in the database.")
194
+
195
+ TableClass = self.row.__class__
196
+
197
+ primary_key = TableClass._meta.primary_key
198
+ primary_key_value = getattr(self.row, primary_key._meta.name)
199
+
200
+ if primary_key_value is None:
201
+ raise ValueError("The primary key is None")
202
+
203
+ columns = [
204
+ TableClass._meta.get_column_by_name(i) if isinstance(i, str) else i
205
+ for i in self.values.keys()
206
+ ]
207
+
208
+ response = (
209
+ await TableClass.update(self.values)
210
+ .where(primary_key == primary_key_value)
211
+ .returning(*columns)
212
+ .run(
213
+ node=node,
214
+ in_pool=in_pool,
215
+ )
216
+ )
217
+
218
+ for key, value in response[0].items():
219
+ setattr(self.row, key, value)
220
+
221
+ def __await__(self) -> t.Generator[None, None, None]:
222
+ """
223
+ If the user doesn't explicity call .run(), proxy to it as a
224
+ convenience.
225
+ """
226
+ return self.run().__await__()
227
+
228
+ def run_sync(self, *args, **kwargs) -> None:
229
+ return run_sync(self.run(*args, **kwargs))
230
+
231
+
176
232
  ###############################################################################
177
233
 
178
234
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import typing as t
4
4
 
5
+ from piccolo.engine.base import BaseBatch
5
6
  from piccolo.query.base import Query
6
7
  from piccolo.querystring import QueryString
7
8
 
@@ -21,6 +22,18 @@ class Raw(Query):
21
22
  super().__init__(table, **kwargs)
22
23
  self.querystring = querystring
23
24
 
25
+ async def batch(
26
+ self,
27
+ batch_size: t.Optional[int] = None,
28
+ node: t.Optional[str] = None,
29
+ **kwargs,
30
+ ) -> BaseBatch:
31
+ if batch_size:
32
+ kwargs.update(batch_size=batch_size)
33
+ if node:
34
+ kwargs.update(node=node)
35
+ return await self.table._meta.db.batch(self, **kwargs)
36
+
24
37
  @property
25
38
  def default_querystrings(self) -> t.Sequence[QueryString]:
26
39
  return [self.querystring]
piccolo/table.py CHANGED
@@ -46,7 +46,7 @@ from piccolo.query import (
46
46
  )
47
47
  from piccolo.query.methods.create_index import CreateIndex
48
48
  from piccolo.query.methods.indexes import Indexes
49
- from piccolo.query.methods.objects import First
49
+ from piccolo.query.methods.objects import First, UpdateSelf
50
50
  from piccolo.query.methods.refresh import Refresh
51
51
  from piccolo.querystring import QueryString
52
52
  from piccolo.utils import _camel_to_snake
@@ -525,6 +525,43 @@ class Table(metaclass=TableMetaclass):
525
525
  == getattr(self, self._meta.primary_key._meta.name)
526
526
  )
527
527
 
528
+ def update_self(
529
+ self, values: t.Dict[t.Union[Column, str], t.Any]
530
+ ) -> UpdateSelf:
531
+ """
532
+ This allows the user to update a single object - useful when the values
533
+ are derived from the database in some way.
534
+
535
+ For example, if we have the following table::
536
+
537
+ class Band(Table):
538
+ name = Varchar()
539
+ popularity = Integer()
540
+
541
+ And we fetch an object::
542
+
543
+ >>> band = await Band.objects().get(name="Pythonistas")
544
+
545
+ We could use the typical syntax for updating the object::
546
+
547
+ >>> band.popularity += 1
548
+ >>> await band.save()
549
+
550
+ The problem with this, is what if another object has already
551
+ incremented ``popularity``? It would overide the value.
552
+
553
+ Instead we can do this:
554
+
555
+ >>> await band.update_self({
556
+ ... Band.popularity: Band.popularity + 1
557
+ ... })
558
+
559
+ This updates ``popularity`` in the database, and also sets the new
560
+ value for ``popularity`` on the object.
561
+
562
+ """
563
+ return UpdateSelf(row=self, values=values)
564
+
528
565
  def remove(self) -> Delete:
529
566
  """
530
567
  A proxy to a delete query.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: piccolo
3
- Version: 1.17.0
3
+ Version: 1.18.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=tmCAvVvU3FZgyBipKPlPcMlzp1gqQfwg1VnJQ-UVzgY,23
1
+ piccolo/__init__.py,sha256=QE23QxQ71q70fSQ-ah5y_ldAqWh6zl7J9GsIUi8fHIk,23
2
2
  piccolo/custom_types.py,sha256=7HMQAze-5mieNLfbQ5QgbRQgR2abR7ol0qehv2SqROY,604
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=yZdURtiVSlxkkEVJoFKAmL2OfNxrUr8xfsfuBBB7IuY,9662
6
6
  piccolo/schema.py,sha256=qNNy4tG_HqnXR9t3hHMgYXtGxHabwQAhUpc6RKLJ_gE,7960
7
- piccolo/table.py,sha256=nS3zuhGNPZ4H9s_E3ieFbrE_u1Tr6TenBVw_nrrtmRQ,49766
7
+ piccolo/table.py,sha256=rwLkom0-4ahOTVon9JDoZ92cp5d3nuwo24ES8attxCM,50896
8
8
  piccolo/table_reflection.py,sha256=jrN1nHerDJ4tU09GtNN3hz7ap-7rXnSUjljFO6LB2H0,7094
9
9
  piccolo/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  piccolo/apps/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -17,7 +17,7 @@ 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=nLPdiLPpCmuIauH2xEjiue-H1rMv0A3YE0O0LmGXAQM,4241
20
+ piccolo/apps/asgi/commands/new.py,sha256=Y9-uw9BdrL_wY_zJhKVTNuVHIE6hDa52YcDgBBA2yII,4248
21
21
  piccolo/apps/asgi/commands/templates/app/README.md.jinja,sha256=As3gNEZt9qcRmTVkjCzNtXJ8r4-3g0fCSe7Q-P39ezI,214
22
22
  piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja,sha256=bgAGe0a9nWk0LAqK3VNDhPcKGqg0z8V-eIX2YmMoZLk,3117
23
23
  piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja,sha256=S-oYY6OFhwJA8PEYnrklQUkqtot3aXTmd7QGrW8Ufn4,2670
@@ -59,7 +59,7 @@ piccolo/apps/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
59
59
  piccolo/apps/migrations/piccolo_app.py,sha256=1EcS2ComBPCaMCC2C3WaPR_GqLwt3XiIJNfm5D2hMgo,593
60
60
  piccolo/apps/migrations/tables.py,sha256=jqBnK-Rk545v1Eu6GaLHTVz7-uwBTUnz2m58OA-mxTc,799
61
61
  piccolo/apps/migrations/auto/__init__.py,sha256=eYb1rZQaalumv_bhbcEe6x3dUglmpFtw7Egg6k7597U,316
62
- piccolo/apps/migrations/auto/diffable_table.py,sha256=FJ_D7FvL_vaThgIY-zFyW_kFmReOI12saM3DFcaAvfw,7058
62
+ piccolo/apps/migrations/auto/diffable_table.py,sha256=1HdqGeWFUYVJ2cJg6DZWOCh67SbgCxFVc554uD7N71A,7405
63
63
  piccolo/apps/migrations/auto/migration_manager.py,sha256=tyAE1Xk6Xb58igbuHw3FGHhKkVjv_Vr5qBH4AbvVb8k,35856
64
64
  piccolo/apps/migrations/auto/operations.py,sha256=169IrCLR3FtTRxHsEHNg6dTG45lcEM7Aoyy3SwgX_hU,1329
65
65
  piccolo/apps/migrations/auto/schema_differ.py,sha256=VA1rK-_wNSdyZZgfA3ZOlpVGJCcvLyouKtT9k2YKhiA,26266
@@ -166,8 +166,8 @@ piccolo/query/methods/drop_index.py,sha256=5x3vHpoOmQ1SMhj6L7snKXX6M9l9j1E1PFSO6
166
166
  piccolo/query/methods/exists.py,sha256=lTMjtrFPFygZmaPV3sfQKXc3K0sVqJ2S6PDc3fRK6YQ,1203
167
167
  piccolo/query/methods/indexes.py,sha256=J-QUqaBJwpgahskUH0Cu0Mq7zEKcfVAtDsUVIVX-C4c,943
168
168
  piccolo/query/methods/insert.py,sha256=ssLJ_wn08KnOwwr7t-VILyn1P4hrvM63CfPIcAJWT5k,4701
169
- piccolo/query/methods/objects.py,sha256=iahDUziUtlx7pJ2uBAhdm3hCTmg2AS9C8cal1my5KR0,11705
170
- piccolo/query/methods/raw.py,sha256=VhYpCB52mZk4zqFTsqK5CHKTDGskUjISXTBV7UjohmA,600
169
+ piccolo/query/methods/objects.py,sha256=XE49RFeInx_RzRD40xzBA5IfmXPmDvKKC6Ql9FV8ntE,13223
170
+ piccolo/query/methods/raw.py,sha256=wQWR8b-yA_Gr-5lqRMZe9BOAAMBAw8CqTx37qVYvM1A,987
171
171
  piccolo/query/methods/refresh.py,sha256=wg1zghKfwz-VmqK4uWa4GNMiDtK-skTqow591Hb3ONM,5854
172
172
  piccolo/query/methods/select.py,sha256=UH-y2g3Ub7bEowfLObrrhw0W-HepTXWCmuMPhk13roE,21406
173
173
  piccolo/query/methods/table_exists.py,sha256=0yb3n6Jd2ovSBWlZ-gl00K4E7Jnbj7J8qAAX5d7hvNk,1259
@@ -211,13 +211,13 @@ tests/apps/meta/commands/test_version.py,sha256=GxBeoC6O3OGICVOWtcCdP3bZza1MR2v9
211
211
  tests/apps/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
212
212
  tests/apps/migrations/test_migration.py,sha256=JmPLtf2BCWX3Yofe0GQe40m8I_yWa_-3vk1lDfFDfIo,308
213
213
  tests/apps/migrations/auto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
214
- tests/apps/migrations/auto/test_diffable_table.py,sha256=bok3G9pwEYnE3AL6UG4iEHrVBZJQ_ovYCdKC3we5JVQ,2932
214
+ tests/apps/migrations/auto/test_diffable_table.py,sha256=p0cKDkfhmu96-rB9bonOlg5bmfQ7U9S2kRppOt4YxyU,3338
215
215
  tests/apps/migrations/auto/test_migration_manager.py,sha256=L2raSuhZybRyK-BEh-G-nDndAgl4sLynekHQFqyrteY,35719
216
216
  tests/apps/migrations/auto/test_schema_differ.py,sha256=UdsaZisA02j15wr1bXkXD6Cqu3p0A23NwFQLXsJdQL4,19391
217
217
  tests/apps/migrations/auto/test_schema_snapshot.py,sha256=ZyvGZqn3N3cwd-3S-FME5AJ8buDSHesw7yPIvY6mE5k,6196
218
218
  tests/apps/migrations/auto/test_serialisation.py,sha256=EFkhES1w9h51UCamWrhxs3mf4I718ggeP7Yl5J_UID4,13548
219
219
  tests/apps/migrations/auto/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
220
- tests/apps/migrations/auto/integration/test_migrations.py,sha256=t6QK3sSYovc4K68tySUo51H3a6LpubM442agiBYQxZ4,46753
220
+ tests/apps/migrations/auto/integration/test_migrations.py,sha256=e4GCAfMWvo5-6EM8f-EmAiLLGR62dxCZ8Tp-0bZ_kDQ,47285
221
221
  tests/apps/migrations/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
222
222
  tests/apps/migrations/commands/test_base.py,sha256=NgHgVjNd3Hil9eODvW7Ic2D9muTa_grNaH3YpRFfR8I,1829
223
223
  tests/apps/migrations/commands/test_check.py,sha256=hOX_sVk1nfCRfbQ8tJoFEUBFhih9O4QuQLHTp5TQaiY,630
@@ -316,7 +316,7 @@ tests/query/mixins/test_order_by_delegate.py,sha256=mOV3Gxs0XeliONxjWSOniI1z6lbZ
316
316
  tests/table/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
317
317
  tests/table/test_all_columns.py,sha256=wZ7i9mTT9wKWLE2BoQ9jDbPaqnBHfV-ZlsROp7SZq7k,667
318
318
  tests/table/test_alter.py,sha256=pMD38BIFfta1vxFqp8YoaRfMxdwxhQSwcxYO4erpUi8,12394
319
- tests/table/test_batch.py,sha256=l3kSHOT2DQ8FJjbr_bFyMGgQ2BI0Ed5Gs4GeJdEsjVo,3795
319
+ tests/table/test_batch.py,sha256=fhBfC5P_vsk1wlmVLeVCDQ8tz9nrny57aIgRnPJ6tlg,4944
320
320
  tests/table/test_callback.py,sha256=mNp4NOQ4PM_PIKn-WLG4HyvN-YHk7w0Ka_Bljg5Ugd4,6381
321
321
  tests/table/test_constructor.py,sha256=zPbzhKQWzzsQQUK7P9WM8OgCi1ndhXedP6rU0tg6XIM,832
322
322
  tests/table/test_count.py,sha256=qm4dwlQJ5gv8FPSsgYTS-3Gsd_KLgvWlFnmXweKydxw,2297
@@ -343,6 +343,7 @@ tests/table/test_select.py,sha256=jgeiahIlNFVijxYb3a54g1sJWVfH3llaYrsTBmdicrs,40
343
343
  tests/table/test_str.py,sha256=eztWNULcjARR1fr9X5n4tojhDNgDfatVyNHwuYrzHAo,1731
344
344
  tests/table/test_table_exists.py,sha256=upv2e9UD32V2QZOShzmcw0reMqRbYiX_jxWx57p25jg,1082
345
345
  tests/table/test_update.py,sha256=Cqi0xX3kEuJ0k-x_emPGB3arXuGWZ9e3CJ3HPFnw9Zw,20505
346
+ tests/table/test_update_self.py,sha256=im6HcM-WLkEhZP0vTL42tYEJZyAZG6gDOxCnbikCBD4,907
346
347
  tests/table/instance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
347
348
  tests/table/instance/test_create.py,sha256=JD0l7L9dDK1FKPhUs6WC_B2bruPR1qQ8aIqXpEbfiUg,1105
348
349
  tests/table/instance/test_get_related.py,sha256=dAT89KHuO_XEMY6h0ngDHrXp_nJsMoSToTbH_IRclTE,1275
@@ -367,9 +368,9 @@ tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5
367
368
  tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
368
369
  tests/utils/test_table_reflection.py,sha256=SIzuat-IpcVj1GCFyOWKShI8YkhdOPPFH7qVrvfyPNE,3794
369
370
  tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
370
- piccolo-1.17.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
371
- piccolo-1.17.0.dist-info/METADATA,sha256=HjbVcxKJlQbf47mpfUFb_AnX1LnlFYOoYvTuTX-iqm4,5178
372
- piccolo-1.17.0.dist-info/WHEEL,sha256=nCVcAvsfA9TDtwGwhYaRrlPhTLV9m-Ga6mdyDtuwK18,91
373
- piccolo-1.17.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
374
- piccolo-1.17.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
375
- piccolo-1.17.0.dist-info/RECORD,,
371
+ piccolo-1.18.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
372
+ piccolo-1.18.0.dist-info/METADATA,sha256=_lAgMQsJ-zW7SJkRvz-KQhmrog7hhhBxojwopzFTA_4,5178
373
+ piccolo-1.18.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
374
+ piccolo-1.18.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
375
+ piccolo-1.18.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
376
+ piccolo-1.18.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -888,6 +888,25 @@ class TestMigrations(MigrationTestCase):
888
888
  ]
889
889
  )
890
890
 
891
+ def test_column_type_conversion_integer_float(self):
892
+ """
893
+ Make sure conversion between ``Integer`` and ``Real`` works - related
894
+ to this bug:
895
+
896
+ https://github.com/piccolo-orm/piccolo/issues/1071
897
+
898
+ """
899
+ self._test_migrations(
900
+ table_snapshots=[
901
+ [self.table(column)]
902
+ for column in [
903
+ Real(default=1.0),
904
+ Integer(default=1),
905
+ Real(default=1.0),
906
+ ]
907
+ ]
908
+ )
909
+
891
910
  def test_column_type_conversion_json(self):
892
911
  self._test_migrations(
893
912
  table_snapshots=[
@@ -73,6 +73,19 @@ class TestCompareDicts(TestCase):
73
73
  response = compare_dicts(dict_1, dict_2)
74
74
  self.assertEqual(response, {"a": OnDelete.set_default})
75
75
 
76
+ def test_numeric_values(self):
77
+ """
78
+ Make sure that if we have two numbers which are equal, but different
79
+ types, then they are identified as being different.
80
+
81
+ https://github.com/piccolo-orm/piccolo/issues/1071
82
+
83
+ """
84
+ dict_1 = {"a": 1}
85
+ dict_2 = {"a": 1.0}
86
+ response = compare_dicts(dict_1, dict_2)
87
+ self.assertEqual(response, {"a": 1})
88
+
76
89
 
77
90
  class TestDiffableTable(TestCase):
78
91
  def test_subtract(self):
tests/table/test_batch.py CHANGED
@@ -95,6 +95,47 @@ class TestBatchObjects(DBTestCase):
95
95
  self.assertEqual(iterations, _iterations)
96
96
 
97
97
 
98
+ class TestBatchRaw(DBTestCase):
99
+ def _check_results(self, batch):
100
+ """
101
+ Make sure the data is returned in the correct format.
102
+ """
103
+ self.assertEqual(type(batch), list)
104
+ if len(batch) > 0:
105
+ row = batch[0]
106
+ self.assertIsInstance(row, Manager)
107
+
108
+ async def run_batch(self, batch_size):
109
+ row_count = 0
110
+ iterations = 0
111
+
112
+ async with await Manager.raw("SELECT * FROM manager").batch(
113
+ batch_size=batch_size
114
+ ) as batch:
115
+ async for _batch in batch:
116
+ self._check_results(_batch)
117
+ _row_count = len(_batch)
118
+ row_count += _row_count
119
+ iterations += 1
120
+
121
+ return row_count, iterations
122
+
123
+ async def test_batch(self):
124
+ row_count = 1000
125
+ self.insert_many_rows(row_count)
126
+
127
+ batch_size = 10
128
+
129
+ _row_count, iterations = asyncio.run(
130
+ self.run_batch(batch_size=batch_size), debug=True
131
+ )
132
+
133
+ _iterations = math.ceil(row_count / batch_size)
134
+
135
+ self.assertEqual(_row_count, row_count)
136
+ self.assertEqual(iterations, _iterations)
137
+
138
+
98
139
  @engines_only("postgres", "cockroach")
99
140
  class TestBatchNodeArg(TestCase):
100
141
  def test_batch_extra_node(self):
@@ -0,0 +1,27 @@
1
+ from piccolo.testing.test_case import AsyncTableTest
2
+ from tests.example_apps.music.tables import Band, Manager
3
+
4
+
5
+ class TestUpdateSelf(AsyncTableTest):
6
+
7
+ tables = [Band, Manager]
8
+
9
+ async def test_update_self(self):
10
+ band = Band({Band.name: "Pythonistas", Band.popularity: 1000})
11
+
12
+ # Make sure we get a ValueError if it's not in the database yet.
13
+ with self.assertRaises(ValueError):
14
+ await band.update_self({Band.popularity: Band.popularity + 1})
15
+
16
+ # Save it, so it's in the database
17
+ await band.save()
18
+
19
+ # Make sure we can successfully update the object
20
+ await band.update_self({Band.popularity: Band.popularity + 1})
21
+
22
+ # Make sure the value was updated on the object
23
+ assert band.popularity == 1001
24
+
25
+ # Make sure the value was updated in the database
26
+ await band.refresh()
27
+ assert band.popularity == 1001