TypeDAL 3.15.1__tar.gz → 3.15.2__tar.gz

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.

Files changed (58) hide show
  1. {typedal-3.15.1 → typedal-3.15.2}/CHANGELOG.md +6 -0
  2. {typedal-3.15.1 → typedal-3.15.2}/PKG-INFO +2 -2
  3. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/__about__.py +1 -1
  4. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/core.py +39 -1
  5. {typedal-3.15.1 → typedal-3.15.2}/tests/test_relationships.py +16 -0
  6. {typedal-3.15.1 → typedal-3.15.2}/.github/workflows/su6.yml +0 -0
  7. {typedal-3.15.1 → typedal-3.15.2}/.gitignore +0 -0
  8. {typedal-3.15.1 → typedal-3.15.2}/.readthedocs.yml +0 -0
  9. {typedal-3.15.1 → typedal-3.15.2}/README.md +0 -0
  10. {typedal-3.15.1 → typedal-3.15.2}/coverage.svg +0 -0
  11. {typedal-3.15.1 → typedal-3.15.2}/docs/1_getting_started.md +0 -0
  12. {typedal-3.15.1 → typedal-3.15.2}/docs/2_defining_tables.md +0 -0
  13. {typedal-3.15.1 → typedal-3.15.2}/docs/3_building_queries.md +0 -0
  14. {typedal-3.15.1 → typedal-3.15.2}/docs/4_relationships.md +0 -0
  15. {typedal-3.15.1 → typedal-3.15.2}/docs/5_py4web.md +0 -0
  16. {typedal-3.15.1 → typedal-3.15.2}/docs/6_migrations.md +0 -0
  17. {typedal-3.15.1 → typedal-3.15.2}/docs/7_mixins.md +0 -0
  18. {typedal-3.15.1 → typedal-3.15.2}/docs/css/code_blocks.css +0 -0
  19. {typedal-3.15.1 → typedal-3.15.2}/docs/index.md +0 -0
  20. {typedal-3.15.1 → typedal-3.15.2}/docs/requirements.txt +0 -0
  21. {typedal-3.15.1 → typedal-3.15.2}/example_new.py +0 -0
  22. {typedal-3.15.1 → typedal-3.15.2}/example_old.py +0 -0
  23. {typedal-3.15.1 → typedal-3.15.2}/mkdocs.yml +0 -0
  24. {typedal-3.15.1 → typedal-3.15.2}/pyproject.toml +0 -0
  25. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/__init__.py +0 -0
  26. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/caching.py +0 -0
  27. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/cli.py +0 -0
  28. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/config.py +0 -0
  29. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/fields.py +0 -0
  30. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/for_py4web.py +0 -0
  31. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/for_web2py.py +0 -0
  32. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/helpers.py +0 -0
  33. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/mixins.py +0 -0
  34. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/py.typed +0 -0
  35. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/serializers/as_json.py +0 -0
  36. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/types.py +0 -0
  37. {typedal-3.15.1 → typedal-3.15.2}/src/typedal/web2py_py4web_shared.py +0 -0
  38. {typedal-3.15.1 → typedal-3.15.2}/tests/__init__.py +0 -0
  39. {typedal-3.15.1 → typedal-3.15.2}/tests/configs/simple.toml +0 -0
  40. {typedal-3.15.1 → typedal-3.15.2}/tests/configs/valid.env +0 -0
  41. {typedal-3.15.1 → typedal-3.15.2}/tests/configs/valid.toml +0 -0
  42. {typedal-3.15.1 → typedal-3.15.2}/tests/test_cli.py +0 -0
  43. {typedal-3.15.1 → typedal-3.15.2}/tests/test_config.py +0 -0
  44. {typedal-3.15.1 → typedal-3.15.2}/tests/test_docs_examples.py +0 -0
  45. {typedal-3.15.1 → typedal-3.15.2}/tests/test_helpers.py +0 -0
  46. {typedal-3.15.1 → typedal-3.15.2}/tests/test_json.py +0 -0
  47. {typedal-3.15.1 → typedal-3.15.2}/tests/test_main.py +0 -0
  48. {typedal-3.15.1 → typedal-3.15.2}/tests/test_mixins.py +0 -0
  49. {typedal-3.15.1 → typedal-3.15.2}/tests/test_mypy.py +0 -0
  50. {typedal-3.15.1 → typedal-3.15.2}/tests/test_orm.py +0 -0
  51. {typedal-3.15.1 → typedal-3.15.2}/tests/test_py4web.py +0 -0
  52. {typedal-3.15.1 → typedal-3.15.2}/tests/test_query_builder.py +0 -0
  53. {typedal-3.15.1 → typedal-3.15.2}/tests/test_row.py +0 -0
  54. {typedal-3.15.1 → typedal-3.15.2}/tests/test_stats.py +0 -0
  55. {typedal-3.15.1 → typedal-3.15.2}/tests/test_table.py +0 -0
  56. {typedal-3.15.1 → typedal-3.15.2}/tests/test_web2py.py +0 -0
  57. {typedal-3.15.1 → typedal-3.15.2}/tests/test_xx_others.py +0 -0
  58. {typedal-3.15.1 → typedal-3.15.2}/tests/timings.py +0 -0
@@ -2,6 +2,12 @@
2
2
 
3
3
  <!--next-version-placeholder-->
4
4
 
5
+ ## v3.15.2 (2025-07-24)
6
+
7
+ ### Fix
8
+
9
+ * Store `._rows` in table entry to access raw data (including untyped relationships via associative table for example) ([`cfdaa09`](https://github.com/trialandsuccess/TypeDAL/commit/cfdaa09c70a7b2056f56b55fbddb6b5e65f9ad33))
10
+
5
11
  ## v3.15.1 (2025-07-14)
6
12
 
7
13
  ### Fix
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: TypeDAL
3
- Version: 3.15.1
3
+ Version: 3.15.2
4
4
  Summary: Typing support for PyDAL
5
5
  Project-URL: Documentation, https://typedal.readthedocs.io/
6
6
  Project-URL: Issues, https://github.com/trialandsuccess/TypeDAL/issues
@@ -5,4 +5,4 @@ This file contains the Version info for this package.
5
5
  # SPDX-FileCopyrightText: 2023-present Robin van der Noord <robinvandernoord@gmail.com>
6
6
  #
7
7
  # SPDX-License-Identifier: MIT
8
- __version__ = "3.15.1"
8
+ __version__ = "3.15.2"
@@ -9,6 +9,7 @@ import functools
9
9
  import inspect
10
10
  import json
11
11
  import math
12
+ import re
12
13
  import sys
13
14
  import types
14
15
  import typing
@@ -1608,6 +1609,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
1608
1609
 
1609
1610
  # set up by 'new':
1610
1611
  _row: Row | None = None
1612
+ _rows: tuple[Row, ...] = ()
1611
1613
 
1612
1614
  _with: list[str]
1613
1615
 
@@ -1980,6 +1982,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1980
1982
  model: Type[T_MetaInstance],
1981
1983
  records: dict[int, T_MetaInstance] = None,
1982
1984
  metadata: Metadata = None,
1985
+ raw: dict[int, list[Row]] = None,
1983
1986
  ) -> None:
1984
1987
  """
1985
1988
  Should not be called manually!
@@ -2005,6 +2008,11 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
2005
2008
  raise NotImplementedError(f"`id` could not be found for {row}")
2006
2009
 
2007
2010
  records = records or {_get_id(row): model(row) for row in rows}
2011
+ raw = raw or {}
2012
+
2013
+ for idx, entity in records.items():
2014
+ entity._rows = tuple(raw.get(idx, []))
2015
+
2008
2016
  super().__init__(rows.db, records, rows.colnames, rows.compact, rows.response, rows.fields)
2009
2017
  self.model = model
2010
2018
  self.metadata = metadata or {}
@@ -2321,6 +2329,29 @@ from .caching import ( # noqa: E402
2321
2329
  )
2322
2330
 
2323
2331
 
2332
+ def normalize_table_keys(row: Row, pattern: re.Pattern = re.compile(r"^([a-zA-Z_]+)_(\d{5,})$")) -> Row:
2333
+ """
2334
+ Normalize table keys in a PyDAL Row object by stripping numeric hash suffixes
2335
+ from table names, only if the suffix is 5 or more digits.
2336
+
2337
+ For example:
2338
+ Row({'articles_12345': {...}}) -> Row({'articles': {...}})
2339
+ Row({'articles_123': {...}}) -> unchanged
2340
+
2341
+ Returns:
2342
+ Row: A new Row object with normalized keys.
2343
+ """
2344
+ new_data: dict[str, Any] = {}
2345
+ for key, value in row.items():
2346
+ if match := pattern.match(key):
2347
+ base, _suffix = match.groups()
2348
+ normalized_key = base
2349
+ new_data[normalized_key] = value
2350
+ else:
2351
+ new_data[key] = value
2352
+ return Row(new_data)
2353
+
2354
+
2324
2355
  class QueryBuilder(typing.Generic[T_MetaInstance]):
2325
2356
  """
2326
2357
  Abstration on top of pydal's query system.
@@ -2864,13 +2895,20 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2864
2895
  db = self._get_db()
2865
2896
  main_table = self.model._ensure_table_defined()
2866
2897
 
2898
+ # id: Model
2867
2899
  records = {}
2900
+
2901
+ # id: [Row]
2902
+ raw_per_id = defaultdict(list)
2903
+
2868
2904
  seen_relations: dict[str, set[str]] = defaultdict(set) # main id -> set of col + id for relation
2869
2905
 
2870
2906
  for row in rows:
2871
2907
  main = row[main_table]
2872
2908
  main_id = main.id
2873
2909
 
2910
+ raw_per_id[main_id].append(normalize_table_keys(row))
2911
+
2874
2912
  if main_id not in records:
2875
2913
  records[main_id] = self.model(main)
2876
2914
  records[main_id]._with = list(self.relationships.keys())
@@ -2915,7 +2953,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2915
2953
  # create single T
2916
2954
  records[main_id][column] = instance
2917
2955
 
2918
- return _to(rows, self.model, records, metadata=metadata)
2956
+ return _to(rows, self.model, records, metadata=metadata, raw=raw_per_id)
2919
2957
 
2920
2958
  def collect_or_fail(self, exception: typing.Optional[Exception] = None) -> "TypedRows[T_MetaInstance]":
2921
2959
  """
@@ -620,3 +620,19 @@ def test_count_with_join():
620
620
 
621
621
  assert User.where(id=4).join("articles").count(User.id) == 1
622
622
  assert User.where(id=4).join("articles").count(Article.id) == 2
623
+
624
+
625
+ def test_accessing_raw_data():
626
+ _setup_data()
627
+
628
+ user = User.where(id=4).join("articles").first()
629
+
630
+ # <User({"id": 4, "name": "Untagged Author", "roles": [], "gid": "967da807-eb13-46dc-a0f3-d5c04751edf4", "main_role": 2, "extra_roles": []}) + ['articles']>
631
+ assert user._row
632
+
633
+ # one row per user+article combination like how postgres returns it:
634
+ assert len(user._rows) == 2
635
+
636
+ assert {row.user.id for row in user._rows} == {4}
637
+
638
+ assert {row.articles.id for row in user._rows} == {1, 2}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes