piccolo 1.17.1__py3-none-any.whl → 1.19.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 +81 -0
- piccolo/query/methods/raw.py +13 -0
- piccolo/query/methods/select.py +34 -0
- piccolo/query/mixins.py +88 -0
- piccolo/table.py +38 -1
- {piccolo-1.17.1.dist-info → piccolo-1.19.0.dist-info}/METADATA +1 -1
- {piccolo-1.17.1.dist-info → piccolo-1.19.0.dist-info}/RECORD +15 -14
- {piccolo-1.17.1.dist-info → piccolo-1.19.0.dist-info}/WHEEL +1 -1
- tests/table/test_batch.py +41 -0
- tests/table/test_select.py +34 -0
- tests/table/test_update_self.py +27 -0
- {piccolo-1.17.1.dist-info → piccolo-1.19.0.dist-info}/LICENSE +0 -0
- {piccolo-1.17.1.dist-info → piccolo-1.19.0.dist-info}/entry_points.txt +0 -0
- {piccolo-1.17.1.dist-info → piccolo-1.19.0.dist-info}/top_level.txt +0 -0
piccolo/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__VERSION__ = "1.
|
1
|
+
__VERSION__ = "1.19.0"
|
piccolo/query/methods/objects.py
CHANGED
@@ -13,6 +13,8 @@ from piccolo.query.mixins import (
|
|
13
13
|
CallbackDelegate,
|
14
14
|
CallbackType,
|
15
15
|
LimitDelegate,
|
16
|
+
LockRowsDelegate,
|
17
|
+
LockStrength,
|
16
18
|
OffsetDelegate,
|
17
19
|
OrderByDelegate,
|
18
20
|
OrderByRaw,
|
@@ -27,6 +29,7 @@ from piccolo.utils.sync import run_sync
|
|
27
29
|
|
28
30
|
if t.TYPE_CHECKING: # pragma: no cover
|
29
31
|
from piccolo.columns import Column
|
32
|
+
from piccolo.table import Table
|
30
33
|
|
31
34
|
|
32
35
|
###############################################################################
|
@@ -173,6 +176,61 @@ class Create(t.Generic[TableInstance]):
|
|
173
176
|
return run_sync(self.run(*args, **kwargs))
|
174
177
|
|
175
178
|
|
179
|
+
class UpdateSelf:
|
180
|
+
|
181
|
+
def __init__(
|
182
|
+
self,
|
183
|
+
row: Table,
|
184
|
+
values: t.Dict[t.Union[Column, str], t.Any],
|
185
|
+
):
|
186
|
+
self.row = row
|
187
|
+
self.values = values
|
188
|
+
|
189
|
+
async def run(
|
190
|
+
self,
|
191
|
+
node: t.Optional[str] = None,
|
192
|
+
in_pool: bool = True,
|
193
|
+
) -> None:
|
194
|
+
if not self.row._exists_in_db:
|
195
|
+
raise ValueError("This row doesn't exist in the database.")
|
196
|
+
|
197
|
+
TableClass = self.row.__class__
|
198
|
+
|
199
|
+
primary_key = TableClass._meta.primary_key
|
200
|
+
primary_key_value = getattr(self.row, primary_key._meta.name)
|
201
|
+
|
202
|
+
if primary_key_value is None:
|
203
|
+
raise ValueError("The primary key is None")
|
204
|
+
|
205
|
+
columns = [
|
206
|
+
TableClass._meta.get_column_by_name(i) if isinstance(i, str) else i
|
207
|
+
for i in self.values.keys()
|
208
|
+
]
|
209
|
+
|
210
|
+
response = (
|
211
|
+
await TableClass.update(self.values)
|
212
|
+
.where(primary_key == primary_key_value)
|
213
|
+
.returning(*columns)
|
214
|
+
.run(
|
215
|
+
node=node,
|
216
|
+
in_pool=in_pool,
|
217
|
+
)
|
218
|
+
)
|
219
|
+
|
220
|
+
for key, value in response[0].items():
|
221
|
+
setattr(self.row, key, value)
|
222
|
+
|
223
|
+
def __await__(self) -> t.Generator[None, None, None]:
|
224
|
+
"""
|
225
|
+
If the user doesn't explicity call .run(), proxy to it as a
|
226
|
+
convenience.
|
227
|
+
"""
|
228
|
+
return self.run().__await__()
|
229
|
+
|
230
|
+
def run_sync(self, *args, **kwargs) -> None:
|
231
|
+
return run_sync(self.run(*args, **kwargs))
|
232
|
+
|
233
|
+
|
176
234
|
###############################################################################
|
177
235
|
|
178
236
|
|
@@ -194,6 +252,7 @@ class Objects(
|
|
194
252
|
"callback_delegate",
|
195
253
|
"prefetch_delegate",
|
196
254
|
"where_delegate",
|
255
|
+
"lock_rows_delegate",
|
197
256
|
)
|
198
257
|
|
199
258
|
def __init__(
|
@@ -213,6 +272,7 @@ class Objects(
|
|
213
272
|
self.prefetch_delegate = PrefetchDelegate()
|
214
273
|
self.prefetch(*prefetch)
|
215
274
|
self.where_delegate = WhereDelegate()
|
275
|
+
self.lock_rows_delegate = LockRowsDelegate()
|
216
276
|
|
217
277
|
def output(self: Self, load_json: bool = False) -> Self:
|
218
278
|
self.output_delegate.output(
|
@@ -272,6 +332,26 @@ class Objects(
|
|
272
332
|
self.limit_delegate.limit(1)
|
273
333
|
return First[TableInstance](query=self)
|
274
334
|
|
335
|
+
def lock_rows(
|
336
|
+
self: Self,
|
337
|
+
lock_strength: t.Union[
|
338
|
+
LockStrength,
|
339
|
+
t.Literal[
|
340
|
+
"UPDATE",
|
341
|
+
"NO KEY UPDATE",
|
342
|
+
"KEY SHARE",
|
343
|
+
"SHARE",
|
344
|
+
],
|
345
|
+
] = LockStrength.update,
|
346
|
+
nowait: bool = False,
|
347
|
+
skip_locked: bool = False,
|
348
|
+
of: t.Tuple[type[Table], ...] = (),
|
349
|
+
) -> Self:
|
350
|
+
self.lock_rows_delegate.lock_rows(
|
351
|
+
lock_strength, nowait, skip_locked, of
|
352
|
+
)
|
353
|
+
return self
|
354
|
+
|
275
355
|
def get(self, where: Combinable) -> Get[TableInstance]:
|
276
356
|
self.where_delegate.where(where)
|
277
357
|
self.limit_delegate.limit(1)
|
@@ -322,6 +402,7 @@ class Objects(
|
|
322
402
|
"offset_delegate",
|
323
403
|
"output_delegate",
|
324
404
|
"order_by_delegate",
|
405
|
+
"lock_rows_delegate",
|
325
406
|
):
|
326
407
|
setattr(select, attr, getattr(self, attr))
|
327
408
|
|
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/query/methods/select.py
CHANGED
@@ -19,6 +19,8 @@ from piccolo.query.mixins import (
|
|
19
19
|
DistinctDelegate,
|
20
20
|
GroupByDelegate,
|
21
21
|
LimitDelegate,
|
22
|
+
LockRowsDelegate,
|
23
|
+
LockStrength,
|
22
24
|
OffsetDelegate,
|
23
25
|
OrderByDelegate,
|
24
26
|
OrderByRaw,
|
@@ -150,6 +152,7 @@ class Select(Query[TableInstance, t.List[t.Dict[str, t.Any]]]):
|
|
150
152
|
"output_delegate",
|
151
153
|
"callback_delegate",
|
152
154
|
"where_delegate",
|
155
|
+
"lock_rows_delegate",
|
153
156
|
)
|
154
157
|
|
155
158
|
def __init__(
|
@@ -174,6 +177,7 @@ class Select(Query[TableInstance, t.List[t.Dict[str, t.Any]]]):
|
|
174
177
|
self.output_delegate = OutputDelegate()
|
175
178
|
self.callback_delegate = CallbackDelegate()
|
176
179
|
self.where_delegate = WhereDelegate()
|
180
|
+
self.lock_rows_delegate = LockRowsDelegate()
|
177
181
|
|
178
182
|
self.columns(*columns_list)
|
179
183
|
|
@@ -219,6 +223,26 @@ class Select(Query[TableInstance, t.List[t.Dict[str, t.Any]]]):
|
|
219
223
|
self.offset_delegate.offset(number)
|
220
224
|
return self
|
221
225
|
|
226
|
+
def lock_rows(
|
227
|
+
self: Self,
|
228
|
+
lock_strength: t.Union[
|
229
|
+
LockStrength,
|
230
|
+
t.Literal[
|
231
|
+
"UPDATE",
|
232
|
+
"NO KEY UPDATE",
|
233
|
+
"KEY SHARE",
|
234
|
+
"SHARE",
|
235
|
+
],
|
236
|
+
] = LockStrength.update,
|
237
|
+
nowait: bool = False,
|
238
|
+
skip_locked: bool = False,
|
239
|
+
of: t.Tuple[type[Table], ...] = (),
|
240
|
+
) -> Self:
|
241
|
+
self.lock_rows_delegate.lock_rows(
|
242
|
+
lock_strength, nowait, skip_locked, of
|
243
|
+
)
|
244
|
+
return self
|
245
|
+
|
222
246
|
async def _splice_m2m_rows(
|
223
247
|
self,
|
224
248
|
response: t.List[t.Dict[str, t.Any]],
|
@@ -618,6 +642,16 @@ class Select(Query[TableInstance, t.List[t.Dict[str, t.Any]]]):
|
|
618
642
|
query += "{}"
|
619
643
|
args.append(self.offset_delegate._offset.querystring)
|
620
644
|
|
645
|
+
if self.lock_rows_delegate._lock_rows:
|
646
|
+
if engine_type == "sqlite":
|
647
|
+
raise NotImplementedError(
|
648
|
+
"SQLite doesn't support row locking e.g. SELECT ... FOR "
|
649
|
+
"UPDATE"
|
650
|
+
)
|
651
|
+
|
652
|
+
query += "{}"
|
653
|
+
args.append(self.lock_rows_delegate._lock_rows.querystring)
|
654
|
+
|
621
655
|
querystring = QueryString(query, *args)
|
622
656
|
|
623
657
|
return [querystring]
|
piccolo/query/mixins.py
CHANGED
@@ -784,3 +784,91 @@ class OnConflictDelegate:
|
|
784
784
|
target=target, action=action_, values=values, where=where
|
785
785
|
)
|
786
786
|
)
|
787
|
+
|
788
|
+
|
789
|
+
class LockStrength(str, Enum):
|
790
|
+
"""
|
791
|
+
Specify lock strength
|
792
|
+
|
793
|
+
https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDATE-SHARE
|
794
|
+
"""
|
795
|
+
|
796
|
+
update = "UPDATE"
|
797
|
+
no_key_update = "NO KEY UPDATE"
|
798
|
+
share = "SHARE"
|
799
|
+
key_share = "KEY SHARE"
|
800
|
+
|
801
|
+
|
802
|
+
@dataclass
|
803
|
+
class LockRows:
|
804
|
+
__slots__ = ("lock_strength", "nowait", "skip_locked", "of")
|
805
|
+
|
806
|
+
lock_strength: LockStrength
|
807
|
+
nowait: bool
|
808
|
+
skip_locked: bool
|
809
|
+
of: t.Tuple[t.Type[Table], ...]
|
810
|
+
|
811
|
+
def __post_init__(self):
|
812
|
+
if not isinstance(self.lock_strength, LockStrength):
|
813
|
+
raise TypeError("lock_strength must be a LockStrength")
|
814
|
+
if not isinstance(self.nowait, bool):
|
815
|
+
raise TypeError("nowait must be a bool")
|
816
|
+
if not isinstance(self.skip_locked, bool):
|
817
|
+
raise TypeError("skip_locked must be a bool")
|
818
|
+
if not isinstance(self.of, tuple) or not all(
|
819
|
+
hasattr(x, "_meta") for x in self.of
|
820
|
+
):
|
821
|
+
raise TypeError("of must be a tuple of Table")
|
822
|
+
if self.nowait and self.skip_locked:
|
823
|
+
raise TypeError(
|
824
|
+
"The nowait option cannot be used with skip_locked"
|
825
|
+
)
|
826
|
+
|
827
|
+
@property
|
828
|
+
def querystring(self) -> QueryString:
|
829
|
+
sql = f" FOR {self.lock_strength.value}"
|
830
|
+
if self.of:
|
831
|
+
tables = ", ".join(
|
832
|
+
i._meta.get_formatted_tablename() for i in self.of
|
833
|
+
)
|
834
|
+
sql += " OF " + tables
|
835
|
+
if self.nowait:
|
836
|
+
sql += " NOWAIT"
|
837
|
+
if self.skip_locked:
|
838
|
+
sql += " SKIP LOCKED"
|
839
|
+
|
840
|
+
return QueryString(sql)
|
841
|
+
|
842
|
+
def __str__(self) -> str:
|
843
|
+
return self.querystring.__str__()
|
844
|
+
|
845
|
+
|
846
|
+
@dataclass
|
847
|
+
class LockRowsDelegate:
|
848
|
+
|
849
|
+
_lock_rows: t.Optional[LockRows] = None
|
850
|
+
|
851
|
+
def lock_rows(
|
852
|
+
self,
|
853
|
+
lock_strength: t.Union[
|
854
|
+
LockStrength,
|
855
|
+
t.Literal[
|
856
|
+
"UPDATE",
|
857
|
+
"NO KEY UPDATE",
|
858
|
+
"KEY SHARE",
|
859
|
+
"SHARE",
|
860
|
+
],
|
861
|
+
] = LockStrength.update,
|
862
|
+
nowait=False,
|
863
|
+
skip_locked=False,
|
864
|
+
of: t.Tuple[type[Table], ...] = (),
|
865
|
+
):
|
866
|
+
lock_strength_: LockStrength
|
867
|
+
if isinstance(lock_strength, LockStrength):
|
868
|
+
lock_strength_ = lock_strength
|
869
|
+
elif isinstance(lock_strength, str):
|
870
|
+
lock_strength_ = LockStrength(lock_strength.upper())
|
871
|
+
else:
|
872
|
+
raise ValueError("Unrecognised `lock_strength` value.")
|
873
|
+
|
874
|
+
self._lock_rows = LockRows(lock_strength_, nowait, skip_locked, of)
|
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=P8Wjra0Mf_3SPYYqkD2tAWe0dnPdqTSizVJGqA8al_Q,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
|
@@ -147,7 +147,7 @@ piccolo/engine/postgres.py,sha256=DekL3KafCdzSAEQ6_EgOiUB1ERXh2xpePYwI9QvmN-c,18
|
|
147
147
|
piccolo/engine/sqlite.py,sha256=KwJc3UttBP_8qSREbLJshqEfROF17ENf0Ju9BwI5_so,25236
|
148
148
|
piccolo/query/__init__.py,sha256=bcsMV4813rMRAIqGv4DxI4eyO4FmpXkDv9dfTk5pt3A,699
|
149
149
|
piccolo/query/base.py,sha256=iI9Fv3oOw7T4ZWZvRKRwdtClvQtSaAepslH24vwxZVA,14616
|
150
|
-
piccolo/query/mixins.py,sha256=
|
150
|
+
piccolo/query/mixins.py,sha256=X9HEYnj6uOjgTkGr4vgqTwN_dokJPzVagwbFx385atQ,24468
|
151
151
|
piccolo/query/proxy.py,sha256=Yq4jNc7IWJvdeO3u7_7iPyRy2WhVj8KsIUcIYHBIi9Q,1839
|
152
152
|
piccolo/query/functions/__init__.py,sha256=pZkzOIh7Sg9HPNOeegOwAS46Oxt31ATlSVmwn-lxCbc,605
|
153
153
|
piccolo/query/functions/aggregate.py,sha256=OdjDjr_zyD4S9UbrZ2C3V5mz4OT2sIfAFAdTGr4WL54,4248
|
@@ -166,10 +166,10 @@ 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=kWHSgnXFVYe-x6tIHstbZvZic6mQCCGES-L_2Icbg-I,13910
|
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
|
-
piccolo/query/methods/select.py,sha256=
|
172
|
+
piccolo/query/methods/select.py,sha256=jNR7CsEPUarffYMsXytKm7iScrQ_nqpRX-5mTPrXfjg,22414
|
173
173
|
piccolo/query/methods/table_exists.py,sha256=0yb3n6Jd2ovSBWlZ-gl00K4E7Jnbj7J8qAAX5d7hvNk,1259
|
174
174
|
piccolo/query/methods/update.py,sha256=LfWqIXEl1aecc0rkVssTFmwyD6wXGhlKcTrUVhtlEsw,3705
|
175
175
|
piccolo/testing/__init__.py,sha256=pRFSqRInfx95AakOq54atmvqoB-ue073q2aR8u8zR40,83
|
@@ -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
|
@@ -339,10 +339,11 @@ tests/table/test_raw.py,sha256=9PTvYngQi41nYd5lKzkJdTqsEcwrdOXcvZjq-W26CwQ,1683
|
|
339
339
|
tests/table/test_ref.py,sha256=eYNRnYHzNMXuMbV3B1ca5EidpIg4500q6hr1ccuVaso,269
|
340
340
|
tests/table/test_refresh.py,sha256=-BaLS6fZiR2RtQaFa7D9WGBjrbrss1-tt5xz1NE_m8E,9250
|
341
341
|
tests/table/test_repr.py,sha256=uahz3_GffGQrf2mDE-4-Pu4AmSLBAyso6-9rbohCl58,446
|
342
|
-
tests/table/test_select.py,sha256=
|
342
|
+
tests/table/test_select.py,sha256=PcLRtHGEZfYNB0Qm2b9P6NFJOH2lWvviRjsr1cEmsMQ,41503
|
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.19.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
|
372
|
+
piccolo-1.19.0.dist-info/METADATA,sha256=sjcIxHmO7mxcywJ7dGiQtleQjBW1uIIBL-ld4CJeVoU,5178
|
373
|
+
piccolo-1.19.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
374
|
+
piccolo-1.19.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
|
375
|
+
piccolo-1.19.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
|
376
|
+
piccolo-1.19.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):
|
tests/table/test_select.py
CHANGED
@@ -1028,6 +1028,40 @@ class TestSelect(DBTestCase):
|
|
1028
1028
|
response, [{"name": "Pythonistas", "popularity_log": 3.0}]
|
1029
1029
|
)
|
1030
1030
|
|
1031
|
+
@pytest.mark.skipif(
|
1032
|
+
is_running_sqlite(),
|
1033
|
+
reason="SQLite doesn't support SELECT ... FOR UPDATE.",
|
1034
|
+
)
|
1035
|
+
def test_lock_rows(self):
|
1036
|
+
"""
|
1037
|
+
Make sure the for_update clause works.
|
1038
|
+
"""
|
1039
|
+
self.insert_rows()
|
1040
|
+
|
1041
|
+
query = Band.select()
|
1042
|
+
self.assertNotIn("FOR UPDATE", query.__str__())
|
1043
|
+
|
1044
|
+
query = query.lock_rows()
|
1045
|
+
self.assertTrue(query.__str__().endswith("FOR UPDATE"))
|
1046
|
+
|
1047
|
+
query = query.lock_rows(lock_strength="KEY SHARE")
|
1048
|
+
self.assertTrue(query.__str__().endswith("FOR KEY SHARE"))
|
1049
|
+
|
1050
|
+
query = query.lock_rows(skip_locked=True)
|
1051
|
+
self.assertTrue(query.__str__().endswith("FOR UPDATE SKIP LOCKED"))
|
1052
|
+
|
1053
|
+
query = query.lock_rows(nowait=True)
|
1054
|
+
self.assertTrue(query.__str__().endswith("FOR UPDATE NOWAIT"))
|
1055
|
+
|
1056
|
+
query = query.lock_rows(of=(Band,))
|
1057
|
+
self.assertTrue(query.__str__().endswith('FOR UPDATE OF "band"'))
|
1058
|
+
|
1059
|
+
with self.assertRaises(TypeError):
|
1060
|
+
query = query.lock_rows(skip_locked=True, nowait=True)
|
1061
|
+
|
1062
|
+
response = query.run_sync()
|
1063
|
+
assert response is not None
|
1064
|
+
|
1031
1065
|
|
1032
1066
|
class TestSelectSecret(TestCase):
|
1033
1067
|
def setUp(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
|