piccolo 1.17.1__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 +1 -1
- piccolo/query/methods/objects.py +56 -0
- piccolo/query/methods/raw.py +13 -0
- piccolo/table.py +38 -1
- {piccolo-1.17.1.dist-info → piccolo-1.18.0.dist-info}/METADATA +1 -1
- {piccolo-1.17.1.dist-info → piccolo-1.18.0.dist-info}/RECORD +12 -11
- {piccolo-1.17.1.dist-info → piccolo-1.18.0.dist-info}/WHEEL +1 -1
- tests/table/test_batch.py +41 -0
- tests/table/test_update_self.py +27 -0
- {piccolo-1.17.1.dist-info → piccolo-1.18.0.dist-info}/LICENSE +0 -0
- {piccolo-1.17.1.dist-info → piccolo-1.18.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.17.1.dist-info → piccolo-1.18.0.dist-info}/top_level.txt +0 -0
piccolo/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__VERSION__ = "1.
|
1
|
+
__VERSION__ = "1.18.0"
|
piccolo/query/methods/objects.py
CHANGED
@@ -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
|
|
piccolo/query/methods/raw.py
CHANGED
@@ -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,10 +1,10 @@
|
|
1
|
-
piccolo/__init__.py,sha256=
|
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=
|
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
|
@@ -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=
|
170
|
-
piccolo/query/methods/raw.py,sha256=
|
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
|
@@ -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=
|
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.
|
371
|
-
piccolo-1.
|
372
|
-
piccolo-1.
|
373
|
-
piccolo-1.
|
374
|
-
piccolo-1.
|
375
|
-
piccolo-1.
|
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,,
|
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
|
File without changes
|
File without changes
|
File without changes
|