TypeDAL 3.16.0__py3-none-any.whl → 3.16.2__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.
Potentially problematic release.
This version of TypeDAL might be problematic. Click here for more details.
- typedal/__about__.py +1 -1
- typedal/core.py +127 -22
- {typedal-3.16.0.dist-info → typedal-3.16.2.dist-info}/METADATA +1 -1
- {typedal-3.16.0.dist-info → typedal-3.16.2.dist-info}/RECORD +6 -6
- {typedal-3.16.0.dist-info → typedal-3.16.2.dist-info}/WHEEL +0 -0
- {typedal-3.16.0.dist-info → typedal-3.16.2.dist-info}/entry_points.txt +0 -0
typedal/__about__.py
CHANGED
typedal/core.py
CHANGED
|
@@ -5,6 +5,7 @@ Core functionality of TypeDAL.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import contextlib
|
|
8
|
+
import copy
|
|
8
9
|
import csv
|
|
9
10
|
import datetime as dt
|
|
10
11
|
import functools
|
|
@@ -18,7 +19,6 @@ import typing
|
|
|
18
19
|
import uuid
|
|
19
20
|
import warnings
|
|
20
21
|
from collections import defaultdict
|
|
21
|
-
from copy import copy
|
|
22
22
|
from decimal import Decimal
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from typing import Any, Optional, Type
|
|
@@ -246,7 +246,8 @@ class Relationship(typing.Generic[To_Type]):
|
|
|
246
246
|
return self
|
|
247
247
|
|
|
248
248
|
warnings.warn(
|
|
249
|
-
"Trying to get data from a relationship object! Did you forget to join it?",
|
|
249
|
+
"Trying to get data from a relationship object! Did you forget to join it?",
|
|
250
|
+
category=RuntimeWarning,
|
|
250
251
|
)
|
|
251
252
|
if self.multiple:
|
|
252
253
|
return []
|
|
@@ -255,7 +256,10 @@ class Relationship(typing.Generic[To_Type]):
|
|
|
255
256
|
|
|
256
257
|
|
|
257
258
|
def relationship(
|
|
258
|
-
_type: typing.Type[To_Type],
|
|
259
|
+
_type: typing.Type[To_Type],
|
|
260
|
+
condition: Condition = None,
|
|
261
|
+
join: JOIN_OPTIONS = None,
|
|
262
|
+
on: OnQuery = None,
|
|
259
263
|
) -> To_Type:
|
|
260
264
|
"""
|
|
261
265
|
Define a relationship to another table, when its id is not stored in the current table.
|
|
@@ -546,7 +550,7 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
546
550
|
|
|
547
551
|
for key, field in typedfields.items():
|
|
548
552
|
# clone every property so it can be re-used across mixins:
|
|
549
|
-
clone = copy(field)
|
|
553
|
+
clone = copy.copy(field)
|
|
550
554
|
setattr(cls, key, clone)
|
|
551
555
|
typedfields[key] = clone
|
|
552
556
|
|
|
@@ -737,7 +741,9 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
737
741
|
|
|
738
742
|
@classmethod
|
|
739
743
|
def _annotation_to_pydal_fieldtype(
|
|
740
|
-
cls,
|
|
744
|
+
cls,
|
|
745
|
+
_ftype: T_annotation,
|
|
746
|
+
mut_kw: typing.MutableMapping[str, Any],
|
|
741
747
|
) -> Optional[str]:
|
|
742
748
|
# ftype can be a union or type. typing.cast is sometimes used to tell mypy when it's not a union.
|
|
743
749
|
ftype = typing.cast(type, _ftype) # cast from Type to type to make mypy happy)
|
|
@@ -824,12 +830,26 @@ class TypeDAL(pydal.DAL): # type: ignore
|
|
|
824
830
|
return to_snake(camel)
|
|
825
831
|
|
|
826
832
|
|
|
833
|
+
def default_representer(field: TypedField[T], value: T, table: Type[TypedTable]) -> str:
|
|
834
|
+
"""
|
|
835
|
+
Simply call field.represent on the value.
|
|
836
|
+
"""
|
|
837
|
+
if represent := getattr(field, "represent", None):
|
|
838
|
+
return field.represent(value, table)
|
|
839
|
+
else:
|
|
840
|
+
return repr(value)
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
TypeDAL.representers.setdefault("rows_render", default_representer)
|
|
844
|
+
|
|
827
845
|
P = typing.ParamSpec("P")
|
|
828
846
|
R = typing.TypeVar("R")
|
|
829
847
|
|
|
830
848
|
|
|
831
849
|
def reorder_fields(
|
|
832
|
-
table: pydal.objects.Table,
|
|
850
|
+
table: pydal.objects.Table,
|
|
851
|
+
fields: typing.Iterable[str | Field | TypedField],
|
|
852
|
+
keep_others: bool = True,
|
|
833
853
|
) -> None:
|
|
834
854
|
"""
|
|
835
855
|
Reorder fields of a pydal table.
|
|
@@ -987,7 +1007,9 @@ class TableMeta(type):
|
|
|
987
1007
|
return self.where(lambda row: row.id.belongs(result)).collect()
|
|
988
1008
|
|
|
989
1009
|
def update_or_insert(
|
|
990
|
-
self: Type[T_MetaInstance],
|
|
1010
|
+
self: Type[T_MetaInstance],
|
|
1011
|
+
query: T_Query | AnyDict = DEFAULT,
|
|
1012
|
+
**values: Any,
|
|
991
1013
|
) -> T_MetaInstance:
|
|
992
1014
|
"""
|
|
993
1015
|
Update a row if query matches, else insert a new one.
|
|
@@ -1010,7 +1032,8 @@ class TableMeta(type):
|
|
|
1010
1032
|
return self(record)
|
|
1011
1033
|
|
|
1012
1034
|
def validate_and_insert(
|
|
1013
|
-
self: Type[T_MetaInstance],
|
|
1035
|
+
self: Type[T_MetaInstance],
|
|
1036
|
+
**fields: Any,
|
|
1014
1037
|
) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
|
|
1015
1038
|
"""
|
|
1016
1039
|
Validate input data and then insert a row.
|
|
@@ -1025,7 +1048,9 @@ class TableMeta(type):
|
|
|
1025
1048
|
return None, result.get("errors")
|
|
1026
1049
|
|
|
1027
1050
|
def validate_and_update(
|
|
1028
|
-
self: Type[T_MetaInstance],
|
|
1051
|
+
self: Type[T_MetaInstance],
|
|
1052
|
+
query: Query,
|
|
1053
|
+
**fields: Any,
|
|
1029
1054
|
) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
|
|
1030
1055
|
"""
|
|
1031
1056
|
Validate input data and then update max 1 row.
|
|
@@ -1045,7 +1070,9 @@ class TableMeta(type):
|
|
|
1045
1070
|
return None, None
|
|
1046
1071
|
|
|
1047
1072
|
def validate_and_update_or_insert(
|
|
1048
|
-
self: Type[T_MetaInstance],
|
|
1073
|
+
self: Type[T_MetaInstance],
|
|
1074
|
+
query: Query,
|
|
1075
|
+
**fields: Any,
|
|
1049
1076
|
) -> tuple[Optional[T_MetaInstance], Optional[dict[str, str]]]:
|
|
1050
1077
|
"""
|
|
1051
1078
|
Validate input data and then update_and_insert (on max 1 row).
|
|
@@ -1262,7 +1289,9 @@ class TableMeta(type):
|
|
|
1262
1289
|
|
|
1263
1290
|
# hooks:
|
|
1264
1291
|
def _hook_once(
|
|
1265
|
-
cls: Type[T_MetaInstance],
|
|
1292
|
+
cls: Type[T_MetaInstance],
|
|
1293
|
+
hooks: list[typing.Callable[P, R]],
|
|
1294
|
+
fn: typing.Callable[P, R],
|
|
1266
1295
|
) -> Type[T_MetaInstance]:
|
|
1267
1296
|
@functools.wraps(fn)
|
|
1268
1297
|
def wraps(*a: P.args, **kw: P.kwargs) -> R:
|
|
@@ -1394,7 +1423,7 @@ class TableMeta(type):
|
|
|
1394
1423
|
"""
|
|
1395
1424
|
return cls._hook_once(cls._after_delete, fn)
|
|
1396
1425
|
|
|
1397
|
-
def reorder_fields(cls, fields:
|
|
1426
|
+
def reorder_fields(cls, *fields: str | Field | TypedField, keep_others: bool = True):
|
|
1398
1427
|
"""
|
|
1399
1428
|
Reorder fields of a typedal table.
|
|
1400
1429
|
|
|
@@ -1456,7 +1485,9 @@ class TypedField(Expression, typing.Generic[T_Value]): # pragma: no cover
|
|
|
1456
1485
|
"""
|
|
1457
1486
|
|
|
1458
1487
|
def __get__(
|
|
1459
|
-
self,
|
|
1488
|
+
self,
|
|
1489
|
+
instance: T_MetaInstance | None,
|
|
1490
|
+
owner: Type[T_MetaInstance],
|
|
1460
1491
|
) -> typing.Union[T_Value, "TypedField[T_Value]"]:
|
|
1461
1492
|
"""
|
|
1462
1493
|
Since this class is a Descriptor field, \
|
|
@@ -1665,7 +1696,9 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1665
1696
|
self.update_record = self._update_record # type: ignore
|
|
1666
1697
|
|
|
1667
1698
|
def __new__(
|
|
1668
|
-
cls,
|
|
1699
|
+
cls,
|
|
1700
|
+
row_or_id: typing.Union[Row, Query, pydal.objects.Set, int, str, None, "TypedTable"] = None,
|
|
1701
|
+
**filters: Any,
|
|
1669
1702
|
) -> Self:
|
|
1670
1703
|
"""
|
|
1671
1704
|
Create a Typed Rows model instance from an existing row, ID or query.
|
|
@@ -1848,7 +1881,9 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
|
|
|
1848
1881
|
return typing.cast(str, table.as_yaml(sanitize))
|
|
1849
1882
|
|
|
1850
1883
|
def _as_dict(
|
|
1851
|
-
self,
|
|
1884
|
+
self,
|
|
1885
|
+
datetime_to_str: bool = False,
|
|
1886
|
+
custom_types: typing.Iterable[type] | type | None = None,
|
|
1852
1887
|
) -> AnyDict:
|
|
1853
1888
|
row = self._ensure_matching_row()
|
|
1854
1889
|
|
|
@@ -2104,7 +2139,9 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
2104
2139
|
return self[max_id]
|
|
2105
2140
|
|
|
2106
2141
|
def find(
|
|
2107
|
-
self,
|
|
2142
|
+
self,
|
|
2143
|
+
f: typing.Callable[[T_MetaInstance], Query],
|
|
2144
|
+
limitby: tuple[int, int] = None,
|
|
2108
2145
|
) -> "TypedRows[T_MetaInstance]":
|
|
2109
2146
|
"""
|
|
2110
2147
|
Returns a new Rows object, a subset of the original object, filtered by the function `f`.
|
|
@@ -2176,7 +2213,10 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
2176
2213
|
return mktable(data, headers)
|
|
2177
2214
|
|
|
2178
2215
|
def group_by_value(
|
|
2179
|
-
self,
|
|
2216
|
+
self,
|
|
2217
|
+
*fields: "str | Field | TypedField[T]",
|
|
2218
|
+
one_result: bool = False,
|
|
2219
|
+
**kwargs: Any,
|
|
2180
2220
|
) -> dict[T, list[T_MetaInstance]]:
|
|
2181
2221
|
"""
|
|
2182
2222
|
Group the rows by a specific field (which will be the dict key).
|
|
@@ -2342,7 +2382,10 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
2342
2382
|
|
|
2343
2383
|
@classmethod
|
|
2344
2384
|
def from_rows(
|
|
2345
|
-
cls,
|
|
2385
|
+
cls,
|
|
2386
|
+
rows: Rows,
|
|
2387
|
+
model: Type[T_MetaInstance],
|
|
2388
|
+
metadata: Metadata = None,
|
|
2346
2389
|
) -> "TypedRows[T_MetaInstance]":
|
|
2347
2390
|
"""
|
|
2348
2391
|
Internal method to convert a Rows object to a TypedRows.
|
|
@@ -2368,6 +2411,59 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
|
|
|
2368
2411
|
self.__dict__.update(state)
|
|
2369
2412
|
# db etc. set after undill by caching.py
|
|
2370
2413
|
|
|
2414
|
+
def render(self, i=None, fields=None) -> typing.Generator[T_MetaInstance, None, None]:
|
|
2415
|
+
"""
|
|
2416
|
+
Takes an index and returns a copy of the indexed row with values
|
|
2417
|
+
transformed via the "represent" attributes of the associated fields.
|
|
2418
|
+
|
|
2419
|
+
Args:
|
|
2420
|
+
i: index. If not specified, a generator is returned for iteration
|
|
2421
|
+
over all the rows.
|
|
2422
|
+
fields: a list of fields to transform (if None, all fields with
|
|
2423
|
+
"represent" attributes will be transformed)
|
|
2424
|
+
"""
|
|
2425
|
+
if i is None:
|
|
2426
|
+
# difference: uses .keys() instead of index
|
|
2427
|
+
return (self.render(i, fields=fields) for i in self.records.keys())
|
|
2428
|
+
|
|
2429
|
+
if not self.db.has_representer("rows_render"): # pragma: no cover
|
|
2430
|
+
raise RuntimeError(
|
|
2431
|
+
"Rows.render() needs a `rows_render` representer in DAL instance",
|
|
2432
|
+
)
|
|
2433
|
+
|
|
2434
|
+
row = copy.deepcopy(self.records[i])
|
|
2435
|
+
keys = list(row)
|
|
2436
|
+
if not fields:
|
|
2437
|
+
fields = [f for f in self.fields if isinstance(f, Field) and f.represent]
|
|
2438
|
+
|
|
2439
|
+
for field in fields:
|
|
2440
|
+
if field._table == self.model._table:
|
|
2441
|
+
row[field.name] = self.db.represent(
|
|
2442
|
+
"rows_render",
|
|
2443
|
+
field,
|
|
2444
|
+
row[field.name],
|
|
2445
|
+
row,
|
|
2446
|
+
)
|
|
2447
|
+
# else: relationship, different logic:
|
|
2448
|
+
|
|
2449
|
+
for relation_name in row._with:
|
|
2450
|
+
if relation := self.model._relationships.get(relation_name):
|
|
2451
|
+
relation_table = relation.table
|
|
2452
|
+
|
|
2453
|
+
relation_row = row[relation_name]
|
|
2454
|
+
for fieldname in relation_row:
|
|
2455
|
+
field = relation_table[fieldname]
|
|
2456
|
+
row[relation_name][fieldname] = self.db.represent(
|
|
2457
|
+
"rows_render",
|
|
2458
|
+
field,
|
|
2459
|
+
relation_row[field.name],
|
|
2460
|
+
relation_row,
|
|
2461
|
+
)
|
|
2462
|
+
|
|
2463
|
+
if self.compact and len(keys) == 1 and keys[0] != "_extra": # pragma: no cover
|
|
2464
|
+
return row[keys[0]]
|
|
2465
|
+
return row
|
|
2466
|
+
|
|
2371
2467
|
|
|
2372
2468
|
from .caching import ( # noqa: E402
|
|
2373
2469
|
_remove_cache,
|
|
@@ -2472,7 +2568,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2472
2568
|
self.select_kwargs,
|
|
2473
2569
|
self.relationships,
|
|
2474
2570
|
self.metadata,
|
|
2475
|
-
]
|
|
2571
|
+
],
|
|
2476
2572
|
)
|
|
2477
2573
|
|
|
2478
2574
|
def _extend(
|
|
@@ -2631,7 +2727,10 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2631
2727
|
return self._extend(relationships=relationships)
|
|
2632
2728
|
|
|
2633
2729
|
def cache(
|
|
2634
|
-
self,
|
|
2730
|
+
self,
|
|
2731
|
+
*deps: Any,
|
|
2732
|
+
expires_at: Optional[dt.datetime] = None,
|
|
2733
|
+
ttl: Optional[int | dt.timedelta] = None,
|
|
2635
2734
|
) -> "QueryBuilder[T_MetaInstance]":
|
|
2636
2735
|
"""
|
|
2637
2736
|
Enable caching for this query to load repeated calls from a dill row \
|
|
@@ -2772,7 +2871,10 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2772
2871
|
return db(query).select(*select_args, **select_kwargs)
|
|
2773
2872
|
|
|
2774
2873
|
def collect(
|
|
2775
|
-
self,
|
|
2874
|
+
self,
|
|
2875
|
+
verbose: bool = False,
|
|
2876
|
+
_to: Type["TypedRows[Any]"] = None,
|
|
2877
|
+
add_id: bool = True,
|
|
2776
2878
|
) -> "TypedRows[T_MetaInstance]":
|
|
2777
2879
|
"""
|
|
2778
2880
|
Execute the built query and turn it into model instances, while handling relationships.
|
|
@@ -2938,7 +3040,10 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
|
|
|
2938
3040
|
return query, select_args
|
|
2939
3041
|
|
|
2940
3042
|
def _collect_with_relationships(
|
|
2941
|
-
self,
|
|
3043
|
+
self,
|
|
3044
|
+
rows: Rows,
|
|
3045
|
+
metadata: Metadata,
|
|
3046
|
+
_to: Type["TypedRows[Any]"],
|
|
2942
3047
|
) -> "TypedRows[T_MetaInstance]":
|
|
2943
3048
|
"""
|
|
2944
3049
|
Transform the raw rows into Typed Table model instances.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
typedal/__about__.py,sha256=
|
|
1
|
+
typedal/__about__.py,sha256=CfP_J5AzovunEQDI3-cq85RLJlXs0vlk17loi0BtJQc,207
|
|
2
2
|
typedal/__init__.py,sha256=Y6LT5UE3HrfWND_drJddYFbDjfnvqQER8MxZiEREFGw,366
|
|
3
3
|
typedal/caching.py,sha256=6YUzUMpan56nSy3D-Jl0FS-8V4LbTnpRSoDJHj6yPYo,11782
|
|
4
4
|
typedal/cli.py,sha256=SnWceLPDd-t90VPHAV9O3RR7JZtpVniT55TqtrRv3VM,19255
|
|
5
5
|
typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
|
|
6
|
-
typedal/core.py,sha256=
|
|
6
|
+
typedal/core.py,sha256=4oyAT2Xuu3A4ROVGeScsshiYYTB2KEQWY3PO5GIhZfY,115008
|
|
7
7
|
typedal/fields.py,sha256=oOmTonXG-g4Lpj5_gSr8GJ-EZIEqO435Fm8-MS_cmoc,7356
|
|
8
8
|
typedal/for_py4web.py,sha256=KIIu8XgnAfRQCJfZCra79k8SInOHiFuLDKUv3hzTJng,1908
|
|
9
9
|
typedal/for_web2py.py,sha256=xn7zo6ImsmTkH6LacbjLQl2oqyBvP0zLqRxEJvMQk1w,1929
|
|
@@ -13,7 +13,7 @@ typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
13
13
|
typedal/types.py,sha256=1FIgv1s0be0E8r5Wd9E1nvDvK4ETV8u2NlfI7_P6UUY,6752
|
|
14
14
|
typedal/web2py_py4web_shared.py,sha256=UYmD0_aK1bSVBt_f3j59Mxq-zOmQNkYkb8sPDUibq68,1539
|
|
15
15
|
typedal/serializers/as_json.py,sha256=3JZlFhPrdvZVFAmH7P5DUAz8-TIk-br0F1CjKG3PFDM,2246
|
|
16
|
-
typedal-3.16.
|
|
17
|
-
typedal-3.16.
|
|
18
|
-
typedal-3.16.
|
|
19
|
-
typedal-3.16.
|
|
16
|
+
typedal-3.16.2.dist-info/METADATA,sha256=MPjhJzWC3x2_DcLXJbSlxlKVqNsKf0xE44mTZL_c9MU,10461
|
|
17
|
+
typedal-3.16.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
+
typedal-3.16.2.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
|
|
19
|
+
typedal-3.16.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|