TypeDAL 4.1.0__tar.gz → 4.2.0__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.
Files changed (66) hide show
  1. {typedal-4.1.0 → typedal-4.2.0}/CHANGELOG.md +6 -0
  2. {typedal-4.1.0 → typedal-4.2.0}/PKG-INFO +1 -1
  3. {typedal-4.1.0 → typedal-4.2.0}/example_old.py +0 -2
  4. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/__about__.py +1 -1
  5. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/cli.py +1 -2
  6. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/query_builder.py +19 -5
  7. {typedal-4.1.0 → typedal-4.2.0}/tests/test_config.py +0 -3
  8. {typedal-4.1.0 → typedal-4.2.0}/tests/test_mypy.py +0 -1
  9. {typedal-4.1.0 → typedal-4.2.0}/tests/test_query_builder.py +32 -20
  10. {typedal-4.1.0 → typedal-4.2.0}/tests/test_relationships.py +27 -26
  11. {typedal-4.1.0 → typedal-4.2.0}/.github/workflows/su6.yml +0 -0
  12. {typedal-4.1.0 → typedal-4.2.0}/.gitignore +0 -0
  13. {typedal-4.1.0 → typedal-4.2.0}/.readthedocs.yml +0 -0
  14. {typedal-4.1.0 → typedal-4.2.0}/README.md +0 -0
  15. {typedal-4.1.0 → typedal-4.2.0}/coverage.svg +0 -0
  16. {typedal-4.1.0 → typedal-4.2.0}/docs/1_getting_started.md +0 -0
  17. {typedal-4.1.0 → typedal-4.2.0}/docs/2_defining_tables.md +0 -0
  18. {typedal-4.1.0 → typedal-4.2.0}/docs/3_building_queries.md +0 -0
  19. {typedal-4.1.0 → typedal-4.2.0}/docs/4_relationships.md +0 -0
  20. {typedal-4.1.0 → typedal-4.2.0}/docs/5_py4web.md +0 -0
  21. {typedal-4.1.0 → typedal-4.2.0}/docs/6_migrations.md +0 -0
  22. {typedal-4.1.0 → typedal-4.2.0}/docs/7_configuration.md +0 -0
  23. {typedal-4.1.0 → typedal-4.2.0}/docs/8_mixins.md +0 -0
  24. {typedal-4.1.0 → typedal-4.2.0}/docs/css/code_blocks.css +0 -0
  25. {typedal-4.1.0 → typedal-4.2.0}/docs/index.md +0 -0
  26. {typedal-4.1.0 → typedal-4.2.0}/docs/requirements.txt +0 -0
  27. {typedal-4.1.0 → typedal-4.2.0}/example_new.py +0 -0
  28. {typedal-4.1.0 → typedal-4.2.0}/mkdocs.yml +0 -0
  29. {typedal-4.1.0 → typedal-4.2.0}/pyproject.toml +0 -0
  30. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/__init__.py +0 -0
  31. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/caching.py +0 -0
  32. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/config.py +0 -0
  33. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/constants.py +0 -0
  34. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/core.py +0 -0
  35. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/define.py +0 -0
  36. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/fields.py +0 -0
  37. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/for_py4web.py +0 -0
  38. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/for_web2py.py +0 -0
  39. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/helpers.py +0 -0
  40. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/mixins.py +0 -0
  41. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/py.typed +0 -0
  42. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/relationships.py +0 -0
  43. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/rows.py +0 -0
  44. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/serializers/as_json.py +0 -0
  45. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/tables.py +0 -0
  46. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/types.py +0 -0
  47. {typedal-4.1.0 → typedal-4.2.0}/src/typedal/web2py_py4web_shared.py +0 -0
  48. {typedal-4.1.0 → typedal-4.2.0}/tests/__init__.py +0 -0
  49. {typedal-4.1.0 → typedal-4.2.0}/tests/configs/simple.toml +0 -0
  50. {typedal-4.1.0 → typedal-4.2.0}/tests/configs/valid.env +0 -0
  51. {typedal-4.1.0 → typedal-4.2.0}/tests/configs/valid.toml +0 -0
  52. {typedal-4.1.0 → typedal-4.2.0}/tests/py314_tests.py +0 -0
  53. {typedal-4.1.0 → typedal-4.2.0}/tests/test_cli.py +0 -0
  54. {typedal-4.1.0 → typedal-4.2.0}/tests/test_docs_examples.py +0 -0
  55. {typedal-4.1.0 → typedal-4.2.0}/tests/test_helpers.py +0 -0
  56. {typedal-4.1.0 → typedal-4.2.0}/tests/test_json.py +0 -0
  57. {typedal-4.1.0 → typedal-4.2.0}/tests/test_main.py +0 -0
  58. {typedal-4.1.0 → typedal-4.2.0}/tests/test_mixins.py +0 -0
  59. {typedal-4.1.0 → typedal-4.2.0}/tests/test_orm.py +0 -0
  60. {typedal-4.1.0 → typedal-4.2.0}/tests/test_py4web.py +0 -0
  61. {typedal-4.1.0 → typedal-4.2.0}/tests/test_row.py +0 -0
  62. {typedal-4.1.0 → typedal-4.2.0}/tests/test_stats.py +0 -0
  63. {typedal-4.1.0 → typedal-4.2.0}/tests/test_table.py +0 -0
  64. {typedal-4.1.0 → typedal-4.2.0}/tests/test_web2py.py +0 -0
  65. {typedal-4.1.0 → typedal-4.2.0}/tests/test_xx_others.py +0 -0
  66. {typedal-4.1.0 → typedal-4.2.0}/tests/timings.py +0 -0
@@ -2,6 +2,12 @@
2
2
 
3
3
  <!--next-version-placeholder-->
4
4
 
5
+ ## v4.2.0 (2025-11-28)
6
+
7
+ ### Feature
8
+
9
+ * Minimal support for using querybuilder on old-style pydal tables ([`ec8baeb`](https://github.com/trialandsuccess/TypeDAL/commit/ec8baebbbaae5a2cb5d48254997a6322a0670d7d))
10
+
5
11
  ## v4.1.0 (2025-11-26)
6
12
 
7
13
  ### Feature
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TypeDAL
3
- Version: 4.1.0
3
+ Version: 4.2.0
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
@@ -1,5 +1,3 @@
1
- import datetime
2
-
3
1
  from pydal import DAL, Field
4
2
 
5
3
  from typedal.helpers import utcnow
@@ -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__ = "4.1.0"
8
+ __version__ = "4.2.0"
@@ -392,8 +392,7 @@ def fake_migrations(
392
392
 
393
393
  previously_migrated = (
394
394
  db(
395
- db.ewh_implemented_features.name.belongs(to_fake)
396
- & (db.ewh_implemented_features.installed == True) # noqa E712
395
+ db.ewh_implemented_features.name.belongs(to_fake) & (db.ewh_implemented_features.installed == True) # noqa E712
397
396
  )
398
397
  .select(db.ewh_implemented_features.name)
399
398
  .column("name")
@@ -22,7 +22,7 @@ from .helpers import (
22
22
  normalize_table_keys,
23
23
  throw,
24
24
  )
25
- from .tables import TypedTable
25
+ from .tables import TableMeta, TypedTable
26
26
  from .types import (
27
27
  CacheMetadata,
28
28
  Condition,
@@ -67,7 +67,8 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
67
67
  MyTable.where(...) -> QueryBuilder[MyTable]
68
68
  """
69
69
  self.model = model
70
- table = model._ensure_table_defined()
70
+ table = self._ensure_table_defined()
71
+
71
72
  default_query = table.id > 0
72
73
  self.query = add_query or default_query
73
74
  self.select_args = select_args or []
@@ -75,6 +76,14 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
75
76
  self.relationships = relationships or {}
76
77
  self.metadata = metadata or {}
77
78
 
79
+ def _ensure_table_defined(self):
80
+ model = self.model
81
+ if hasattr(model, "_ensure_table_defined"):
82
+ return model._ensure_table_defined()
83
+ else:
84
+ # already a pydal table
85
+ return model
86
+
78
87
  def __str__(self) -> str:
79
88
  """
80
89
  Simple string representation for the query builder.
@@ -99,7 +108,7 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
99
108
  """
100
109
  Querybuilder is truthy if it has t.Any conditions.
101
110
  """
102
- table = self.model._ensure_table_defined()
111
+ table = self._ensure_table_defined()
103
112
  default_query = table.id > 0
104
113
  return any(
105
114
  [
@@ -191,7 +200,7 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
191
200
  .where(lambda table: table.id == 5, lambda table: table.id == 6) == (table.id == 5) | (table.id=6)
192
201
  """
193
202
  new_query = self.query
194
- table = self.model._ensure_table_defined()
203
+ table = self._ensure_table_defined()
195
204
 
196
205
  queries_or_lambdas = (
197
206
  *queries_or_lambdas,
@@ -531,6 +540,11 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
531
540
  if _to is None:
532
541
  _to = TypedRows
533
542
 
543
+ if not isinstance(self.model, TableMeta):
544
+ # tried to use querybuilder with a non-typedal table,
545
+ # fallback to execute:
546
+ return self.execute(add_id=add_id)
547
+
534
548
  db = self._get_db()
535
549
  metadata = self.metadata.copy()
536
550
 
@@ -792,7 +806,7 @@ class QueryBuilder(t.Generic[T_MetaInstance]):
792
806
  Transform the raw rows into Typed Table model instances with nested relationships.
793
807
  """
794
808
  db = self._get_db()
795
- main_table = self.model._ensure_table_defined()
809
+ main_table = self._ensure_table_defined()
796
810
 
797
811
  # id: Model
798
812
  records: dict[t.Any, T_MetaInstance] = {}
@@ -6,10 +6,7 @@ import uuid
6
6
  from pathlib import Path
7
7
 
8
8
  import pytest
9
-
10
- # from contextlib import chdir
11
9
  from contextlib_chdir import chdir
12
- from pydal2sql import generate_sql
13
10
  from testcontainers.postgres import PostgresContainer
14
11
 
15
12
  from src.typedal import TypeDAL, TypedField, TypedTable
@@ -1,4 +1,3 @@
1
- import sys
2
1
  import typing
3
2
 
4
3
  import pydal.objects
@@ -1,11 +1,8 @@
1
- import inspect
2
- import typing
3
-
4
1
  import pytest
5
2
  from pydal.objects import Query
6
3
 
7
4
  from src.typedal import TypeDAL, TypedField, TypedTable, relationship
8
- from typedal.types import CacheFn, CacheModel, CacheTuple, Rows
5
+ from typedal import QueryBuilder
9
6
 
10
7
  db = TypeDAL("sqlite:memory")
11
8
 
@@ -17,7 +14,9 @@ class TestQueryTable(TypedTable):
17
14
  yet_another = TypedField(list[str], default=["something", "and", "other", "things"])
18
15
 
19
16
  relations = relationship(
20
- list["TestRelationship"], condition=lambda self, other: self.id == other.querytable, join="left"
17
+ list["TestRelationship"],
18
+ condition=lambda self, other: self.id == other.querytable,
19
+ join="left",
21
20
  )
22
21
 
23
22
 
@@ -47,21 +46,21 @@ def test_query_type():
47
46
 
48
47
 
49
48
  """
50
- SELECT "test_query_table"."id",
51
- "test_query_table"."number",
52
- "relations_8106139955393"."id",
53
- "relations_8106139955393"."name",
54
- "relations_8106139955393"."value",
55
- "relations_8106139955393"."querytable"
56
- FROM "test_query_table"
57
- LEFT JOIN "test_relationship" AS "relations_8106139955393"
58
- ON ("relations_8106139955393"."querytable" = "test_query_table"."id")
59
- WHERE ("test_query_table"."id" IN (SELECT "test_query_table"."id"
60
- FROM "test_query_table"
61
- WHERE ("test_query_table"."id" > 0)
62
- ORDER BY "test_query_table"."id"
63
- LIMIT 3 OFFSET 0))
64
- ORDER BY "test_query_table"."number" DESC;
49
+ SELECT "test_query_table"."id"
50
+ , "test_query_table"."number"
51
+ , "relations_8106139955393"."id"
52
+ , "relations_8106139955393"."name"
53
+ , "relations_8106139955393"."value"
54
+ , "relations_8106139955393"."querytable"
55
+ FROM "test_query_table"
56
+ LEFT JOIN "test_relationship" AS "relations_8106139955393"
57
+ ON ("relations_8106139955393"."querytable" = "test_query_table"."id")
58
+ WHERE ("test_query_table"."id" IN (SELECT "test_query_table"."id"
59
+ FROM "test_query_table"
60
+ WHERE ("test_query_table"."id" > 0)
61
+ ORDER BY "test_query_table"."id"
62
+ LIMIT 3 OFFSET 0))
63
+ ORDER BY "test_query_table"."number" DESC;
65
64
  """
66
65
 
67
66
 
@@ -532,3 +531,16 @@ def test_collect_with_extra_fields():
532
531
 
533
532
  with pytest.raises(HTTP):
534
533
  TestRelationship.where(TestRelationship.id == 3245892384).first_or_fail(HTTP(404))
534
+
535
+
536
+ def test_minimal_functionality_on_pydal_style_tables():
537
+ _setup_data()
538
+
539
+ qb1 = TestQueryTable.where(number=2).collect()
540
+ qb2 = QueryBuilder(db.test_query_table).where(number=2).collect()
541
+
542
+ assert len(qb1) == len(qb2)
543
+ assert qb1.first().id == qb2.first().id
544
+
545
+ assert qb2
546
+ assert len(qb2) == 1
@@ -1,5 +1,4 @@
1
1
  import contextlib
2
- import json
3
2
  import time
4
3
  import types
5
4
  import typing
@@ -102,8 +101,7 @@ class Tagged(TypedTable): # pivot table
102
101
 
103
102
 
104
103
  @db.define()
105
- class Empty(TypedTable):
106
- ...
104
+ class Empty(TypedTable): ...
107
105
 
108
106
 
109
107
  def _setup_data():
@@ -314,8 +312,7 @@ def test_typedal_way():
314
312
  author1 = User.where(id=4).join("articles").first()
315
313
 
316
314
  assert (
317
- len(author1.as_dict()["articles"]) == len(author1.__dict__["articles"]) == len(
318
- dict(author1)["articles"]) == 2
315
+ len(author1.as_dict()["articles"]) == len(author1.__dict__["articles"]) == len(dict(author1)["articles"]) == 2
319
316
  )
320
317
 
321
318
 
@@ -483,12 +480,12 @@ def test_caching():
483
480
  cached_user_only2 = User.join().cache(User.id).collect_or_fail()
484
481
 
485
482
  assert (
486
- len(uncached2)
487
- == len(uncached)
488
- == len(cached2)
489
- == len(cached)
490
- == len(cached_user_only2)
491
- == len(cached_user_only)
483
+ len(uncached2)
484
+ == len(uncached)
485
+ == len(cached2)
486
+ == len(cached)
487
+ == len(cached_user_only2)
488
+ == len(cached_user_only)
492
489
  )
493
490
 
494
491
  assert uncached.as_json() == uncached2.as_json() == cached.as_json() == cached2.as_json()
@@ -496,9 +493,9 @@ def test_caching():
496
493
  assert cached.first().gid == cached2.first().gid
497
494
 
498
495
  assert (
499
- [_.name for _ in uncached2.first().roles]
500
- == [_.name for _ in cached.first().roles]
501
- == [_.name for _ in cached2.first().roles]
496
+ [_.name for _ in uncached2.first().roles]
497
+ == [_.name for _ in cached.first().roles]
498
+ == [_.name for _ in cached2.first().roles]
502
499
  )
503
500
 
504
501
  assert not uncached2.metadata.get("cache", {}).get("enabled")
@@ -636,27 +633,31 @@ def test_caching_dependencies():
636
633
 
637
634
  def test_illegal():
638
635
  with pytest.raises(ValueError), pytest.warns(UserWarning):
636
+
639
637
  class HasRelationship:
640
638
  something = relationship("...", condition=lambda: 1, on=lambda: 2)
641
639
 
642
640
  with pytest.raises(ValueError), pytest.warns(UserWarning):
643
641
  Tag.join(Tag.articles, condition=lambda: 1, on=lambda: 2)
644
642
 
643
+
645
644
  def test_join_relationship_custom_on():
646
645
  _setup_data()
647
646
 
648
- rows1 = Tag.join(Tag.articles,
649
- condition=lambda tag, article: (Tagged.tag == tag.id) & (article.gid == Tagged.entity) & (article.author == 3),
650
- method="inner",
651
- )
652
-
653
- rows2 = Tag.join(Tag.articles,
654
- on=lambda tag, article: [
655
- tagged := Tagged.unique_alias(),
656
- (tagged.tag == tag.id) & (article.gid == tagged.entity) & (article.author == 3)
657
- ],
658
- method="inner",
659
- )
647
+ rows1 = Tag.join(
648
+ Tag.articles,
649
+ condition=lambda tag, article: (Tagged.tag == tag.id) & (article.gid == Tagged.entity) & (article.author == 3),
650
+ method="inner",
651
+ )
652
+
653
+ rows2 = Tag.join(
654
+ Tag.articles,
655
+ on=lambda tag, article: [
656
+ tagged := Tagged.unique_alias(),
657
+ (tagged.tag == tag.id) & (article.gid == tagged.entity) & (article.author == 3),
658
+ ],
659
+ method="inner",
660
+ )
660
661
 
661
662
  assert all([row.articles for row in rows1])
662
663
  assert all([row.articles for row in rows2])
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes