TypeDAL 4.9.2__tar.gz → 4.9.4__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.
- typedal-4.9.4/.crush/.gitignore +1 -0
- typedal-4.9.4/.crush/crush.db-shm +0 -0
- typedal-4.9.4/.crush/crush.db-wal +0 -0
- typedal-4.9.4/.crush/logs/crush.log +37 -0
- {typedal-4.9.2 → typedal-4.9.4}/CHANGELOG.md +12 -0
- {typedal-4.9.2 → typedal-4.9.4}/PKG-INFO +1 -1
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/__about__.py +1 -1
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/query_builder.py +70 -5
- typedal-4.9.4/tests/__init__.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_query_builder.py +43 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_relationships.py +27 -0
- /typedal-4.9.2/src/typedal/py.typed → /typedal-4.9.4/.crush/init +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/.github/workflows/su6.yml +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/.gitignore +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/.readthedocs.yml +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/README.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/coverage.svg +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/10_advanced_apis.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/1_getting_started.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/2_defining_tables.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/3_building_queries.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/4_relationships.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/5_py4web.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/6_migrations.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/7_configuration.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/8_mixins.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/9_memoization.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/css/code_blocks.css +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/index.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/docs/requirements.txt +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/example_new.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/example_old.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/mkdocs.yml +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/pyproject.toml +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/__init__.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/caching.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/cli.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/config.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/constants.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/core.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/define.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/enum_helpers.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/fields.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/for_py4web.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/for_web2py.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/helpers.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/mixins.py +0 -0
- /typedal-4.9.2/tests/__init__.py → /typedal-4.9.4/src/typedal/py.typed +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/relationships.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/rows.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/serializers/typescript.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/tables.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/types.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tasks.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/configs/simple.toml +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/configs/valid.env +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/configs/valid.toml +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/conftest.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/py314_tests.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_cli.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_config.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_docs_examples.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_helpers.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_json.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_main.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_mixins.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_mypy.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_orm.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_py4web.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_row.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_stats.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_table.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_typescript.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_typing_mypy.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_typing_pyright.md +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_web2py.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/test_xx_others.py +0 -0
- {typedal-4.9.2 → typedal-4.9.4}/tests/timings.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{"time":"2026-01-20T14:19:21.359487106+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":55},"msg":"Fetching providers from Catwalk"}
|
|
2
|
+
{"time":"2026-01-20T14:19:21.518115889+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":63},"msg":"Catwalk providers not modified"}
|
|
3
|
+
{"time":"2026-01-20T14:19:21.52626179+01:00","level":"INFO","msg":"OK 20250424200609_initial.sql (1.52ms)"}
|
|
4
|
+
{"time":"2026-01-20T14:19:21.526651475+01:00","level":"INFO","msg":"OK 20250515105448_add_summary_message_id.sql (330.71µs)"}
|
|
5
|
+
{"time":"2026-01-20T14:19:21.526995425+01:00","level":"INFO","msg":"OK 20250624000000_add_created_at_indexes.sql (325.29µs)"}
|
|
6
|
+
{"time":"2026-01-20T14:19:21.527303588+01:00","level":"INFO","msg":"OK 20250627000000_add_provider_to_messages.sql (293.15µs)"}
|
|
7
|
+
{"time":"2026-01-20T14:19:21.52790648+01:00","level":"INFO","msg":"OK 20250810000000_add_is_summary_message.sql (495.71µs)"}
|
|
8
|
+
{"time":"2026-01-20T14:19:21.528316924+01:00","level":"INFO","msg":"OK 20250812000000_add_todos_to_sessions.sql (389.38µs)"}
|
|
9
|
+
{"time":"2026-01-20T14:19:21.528324939+01:00","level":"INFO","msg":"goose: successfully migrated database to version: 20250812000000"}
|
|
10
|
+
{"time":"2026-01-20T14:19:21.528356959+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).initLSPClients","file":"github.com/charmbracelet/crush/internal/app/lsp.go","line":21},"msg":"LSP clients initialization started in background"}
|
|
11
|
+
{"time":"2026-01-20T14:19:21.52843831+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.New.func1","file":"github.com/charmbracelet/crush/internal/app/app.go","line":102},"msg":"Initializing MCP clients"}
|
|
12
|
+
{"time":"2026-01-20T14:21:06.413595152+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
13
|
+
{"time":"2026-01-20T14:21:14.539289381+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
14
|
+
{"time":"2026-01-20T14:21:15.710688424+01:00","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/agent/tools.init.func1","file":"github.com/charmbracelet/crush/internal/agent/tools/rg.go","line":18},"msg":"Ripgrep (rg) not found in $PATH. Some grep features might be limited or slower."}
|
|
15
|
+
{"time":"2026-01-20T14:21:17.08089755+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
16
|
+
{"time":"2026-01-20T14:21:17.151106375+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
17
|
+
{"time":"2026-01-20T14:22:05.503761296+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
18
|
+
{"time":"2026-01-20T14:26:05.030425805+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
19
|
+
{"time":"2026-01-20T14:36:26.778665189+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
20
|
+
{"time":"2026-01-20T14:38:51.314473736+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
21
|
+
{"time":"2026-01-20T15:02:22.602405814+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).Shutdown.func1","file":"github.com/charmbracelet/crush/internal/app/app.go","line":403},"msg":"Shutdown took 6.62949ms"}
|
|
22
|
+
{"time":"2026-01-20T15:02:23.719585648+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":55},"msg":"Fetching providers from Catwalk"}
|
|
23
|
+
{"time":"2026-01-20T15:02:23.881789126+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":63},"msg":"Catwalk providers not modified"}
|
|
24
|
+
{"time":"2026-01-20T15:02:23.884044691+01:00","level":"INFO","msg":"goose: no migrations to run. current version: 20250812000000"}
|
|
25
|
+
{"time":"2026-01-20T15:02:23.884082792+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).initLSPClients","file":"github.com/charmbracelet/crush/internal/app/lsp.go","line":21},"msg":"LSP clients initialization started in background"}
|
|
26
|
+
{"time":"2026-01-20T15:02:23.884273487+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.New.func1","file":"github.com/charmbracelet/crush/internal/app/app.go","line":102},"msg":"Initializing MCP clients"}
|
|
27
|
+
{"time":"2026-01-20T15:04:13.024457515+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
28
|
+
{"time":"2026-01-20T15:04:14.232072885+01:00","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/agent/tools.init.func1","file":"github.com/charmbracelet/crush/internal/agent/tools/rg.go","line":18},"msg":"Ripgrep (rg) not found in $PATH. Some grep features might be limited or slower."}
|
|
29
|
+
{"time":"2026-01-20T15:11:24.612161916+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).Shutdown.func1","file":"github.com/charmbracelet/crush/internal/app/app.go","line":403},"msg":"Shutdown took 9.537141ms"}
|
|
30
|
+
{"time":"2026-01-20T15:11:25.263960575+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":55},"msg":"Fetching providers from Catwalk"}
|
|
31
|
+
{"time":"2026-01-20T15:11:25.431990051+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":63},"msg":"Catwalk providers not modified"}
|
|
32
|
+
{"time":"2026-01-20T15:11:25.434282035+01:00","level":"INFO","msg":"goose: no migrations to run. current version: 20250812000000"}
|
|
33
|
+
{"time":"2026-01-20T15:11:25.434366662+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).initLSPClients","file":"github.com/charmbracelet/crush/internal/app/lsp.go","line":21},"msg":"LSP clients initialization started in background"}
|
|
34
|
+
{"time":"2026-01-20T15:11:25.434579879+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.New.func1","file":"github.com/charmbracelet/crush/internal/app/app.go","line":102},"msg":"Initializing MCP clients"}
|
|
35
|
+
{"time":"2026-01-20T15:24:40.456010578+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
36
|
+
{"time":"2026-01-20T15:24:51.673808433+01:00","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/agent/tools.init.func1","file":"github.com/charmbracelet/crush/internal/agent/tools/rg.go","line":18},"msg":"Ripgrep (rg) not found in $PATH. Some grep features might be limited or slower."}
|
|
37
|
+
{"time":"2026-01-20T15:26:03.868420566+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).Cancel","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":907},"msg":"Request cancellation initiated","session_id":"4c2d1684-8db2-4ffd-83bb-9c08fe850f29"}
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.9.4 (2026-07-03)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* **query_builder:** Include orderby fields in distinct-id subquery for paginated joins ([`d343a43`](https://github.com/trialandsuccess/TypeDAL/commit/d343a4326fdbf93fb5fd8b5db061ea5b3355e3de))
|
|
10
|
+
|
|
11
|
+
## v4.9.3 (2026-07-03)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
* Pagination count with joined relationships ([`2eaefc0`](https://github.com/trialandsuccess/TypeDAL/commit/2eaefc051a94d4a4204164e0773a8d7691aae63e))
|
|
16
|
+
|
|
5
17
|
## v4.9.2 (2026-07-02)
|
|
6
18
|
|
|
7
19
|
### Fix
|
|
@@ -818,6 +818,46 @@ class QueryBuilder[T_MetaInstance: _TypedTable]:
|
|
|
818
818
|
|
|
819
819
|
return joins
|
|
820
820
|
|
|
821
|
+
def _selectable_orderby_fields(self, orderby: OrderBy | t.Iterable[OrderBy] | None) -> list[OrderBy]:
|
|
822
|
+
"""Extract field values from pydal orderby expressions."""
|
|
823
|
+
if not orderby:
|
|
824
|
+
return []
|
|
825
|
+
|
|
826
|
+
if isinstance(orderby, (list, tuple, set)):
|
|
827
|
+
return [field for item in orderby for field in self._selectable_orderby_fields(item)]
|
|
828
|
+
|
|
829
|
+
if isinstance(orderby, pydal.objects.Field):
|
|
830
|
+
return [orderby]
|
|
831
|
+
|
|
832
|
+
fields = []
|
|
833
|
+
first = getattr(orderby, "first", None)
|
|
834
|
+
second = getattr(orderby, "second", None)
|
|
835
|
+
|
|
836
|
+
if first is not None:
|
|
837
|
+
fields.extend(self._selectable_orderby_fields(first))
|
|
838
|
+
if second is not None:
|
|
839
|
+
fields.extend(self._selectable_orderby_fields(second))
|
|
840
|
+
|
|
841
|
+
return fields
|
|
842
|
+
|
|
843
|
+
def _select_distinct_ids_with_orderby_fields(self, query: Query, select_kwargs: SelectKwargs) -> str:
|
|
844
|
+
db = self._get_db()
|
|
845
|
+
model = self.model
|
|
846
|
+
select_args: list[OrderBy] = [model.id]
|
|
847
|
+
seen = {str(model.id)}
|
|
848
|
+
|
|
849
|
+
for field in self._selectable_orderby_fields(select_kwargs.get("orderby")):
|
|
850
|
+
key = str(field)
|
|
851
|
+
if key not in seen:
|
|
852
|
+
select_args.append(field)
|
|
853
|
+
seen.add(key)
|
|
854
|
+
|
|
855
|
+
ids = db(query)._select(*select_args, **select_kwargs).rstrip(";")
|
|
856
|
+
id_column = getattr(model.id, "_raw_rname", model.id.name)
|
|
857
|
+
return f'SELECT "{id_column}" FROM ({ids}) AS typedal_paginate_ids' # nosec:
|
|
858
|
+
# id_column originates from code
|
|
859
|
+
# ids is a safe subquery, originating from code
|
|
860
|
+
|
|
821
861
|
def _apply_limitby_optimization(
|
|
822
862
|
self,
|
|
823
863
|
query: Query,
|
|
@@ -837,8 +877,13 @@ class QueryBuilder[T_MetaInstance: _TypedTable]:
|
|
|
837
877
|
|
|
838
878
|
if joins:
|
|
839
879
|
kwargs["join"] = joins
|
|
880
|
+
kwargs["distinct"] = True
|
|
881
|
+
|
|
882
|
+
if joins and kwargs.get("orderby"):
|
|
883
|
+
ids = self._select_distinct_ids_with_orderby_fields(query, kwargs)
|
|
884
|
+
else:
|
|
885
|
+
ids = db(query)._select(model.id, **kwargs)
|
|
840
886
|
|
|
841
|
-
ids = db(query)._select(model.id, **kwargs)
|
|
842
887
|
query = model.id.belongs(ids)
|
|
843
888
|
metadata["ids"] = ids
|
|
844
889
|
|
|
@@ -1128,19 +1173,31 @@ class QueryBuilder[T_MetaInstance: _TypedTable]:
|
|
|
1128
1173
|
"""
|
|
1129
1174
|
yield from self.collect()
|
|
1130
1175
|
|
|
1131
|
-
def __count(
|
|
1176
|
+
def __count(
|
|
1177
|
+
self,
|
|
1178
|
+
db: TypeDAL,
|
|
1179
|
+
distinct: t.Optional[bool] = None,
|
|
1180
|
+
*,
|
|
1181
|
+
include_left_for_distinct: bool = True,
|
|
1182
|
+
) -> Query:
|
|
1132
1183
|
# internal, shared logic between .count and ._count
|
|
1133
1184
|
model = self.model
|
|
1134
1185
|
query = self.query
|
|
1135
1186
|
for key, relation in self.relationships.items():
|
|
1136
|
-
if
|
|
1187
|
+
if not relation.condition:
|
|
1188
|
+
continue
|
|
1189
|
+
|
|
1190
|
+
include_left_join = distinct and include_left_for_distinct
|
|
1191
|
+
if relation.join != "inner" and not include_left_join:
|
|
1137
1192
|
continue
|
|
1138
1193
|
|
|
1139
1194
|
other = relation.get_table(db)
|
|
1140
1195
|
if not distinct:
|
|
1141
1196
|
# todo: can this lead to other issues?
|
|
1142
1197
|
other = other.with_alias(f"{key}_{hash(relation)}")
|
|
1143
|
-
|
|
1198
|
+
|
|
1199
|
+
if relation.condition is not None:
|
|
1200
|
+
query &= relation.condition(model, other)
|
|
1144
1201
|
|
|
1145
1202
|
return query
|
|
1146
1203
|
|
|
@@ -1175,12 +1232,20 @@ class QueryBuilder[T_MetaInstance: _TypedTable]:
|
|
|
1175
1232
|
require_permission(self._permissions, "read")
|
|
1176
1233
|
return bool(self.count())
|
|
1177
1234
|
|
|
1235
|
+
def __pagination_count(self) -> int:
|
|
1236
|
+
if not self.relationships:
|
|
1237
|
+
return self.count()
|
|
1238
|
+
|
|
1239
|
+
db = self._get_db()
|
|
1240
|
+
query = self.__count(db, distinct=self.model.id, include_left_for_distinct=False)
|
|
1241
|
+
return db(query).count(self.model.id)
|
|
1242
|
+
|
|
1178
1243
|
def __paginate(
|
|
1179
1244
|
self,
|
|
1180
1245
|
limit: int,
|
|
1181
1246
|
page: int = 1,
|
|
1182
1247
|
) -> "QueryBuilder[T_MetaInstance]":
|
|
1183
|
-
available = self.
|
|
1248
|
+
available = self.__pagination_count()
|
|
1184
1249
|
|
|
1185
1250
|
_from = limit * (page - 1)
|
|
1186
1251
|
_to = (limit * page) if limit else available
|
|
File without changes
|
|
@@ -519,6 +519,10 @@ def test_orderby():
|
|
|
519
519
|
base_qt = TestQueryTable.select(TestQueryTable.id, TestQueryTable.number)
|
|
520
520
|
|
|
521
521
|
assert base_qt.count() == 5
|
|
522
|
+
assert base_qt._selectable_orderby_fields(None) == []
|
|
523
|
+
|
|
524
|
+
composite_fields = base_qt._selectable_orderby_fields(TestQueryTable.number | TestQueryTable.other)
|
|
525
|
+
assert composite_fields == [TestQueryTable.number, TestQueryTable.other]
|
|
522
526
|
|
|
523
527
|
rows1 = base_qt.orderby(TestQueryTable.id).paginate(limit=3, page=1)
|
|
524
528
|
rows2 = base_qt.select(orderby=TestQueryTable.id, limitby=(0, 3)).collect()
|
|
@@ -602,6 +606,45 @@ def test_select_kwargs_use_rname_psql(dal_psql: TypeDAL):
|
|
|
602
606
|
assert "some_table.some_name" in sql
|
|
603
607
|
|
|
604
608
|
|
|
609
|
+
def test_paginate_inner_joins_with_related_orderby_psql(dal_psql: TypeDAL):
|
|
610
|
+
db = dal_psql
|
|
611
|
+
|
|
612
|
+
@db.define()
|
|
613
|
+
class Method(TypedTable):
|
|
614
|
+
gid = TypedField(str)
|
|
615
|
+
name = TypedField(str)
|
|
616
|
+
|
|
617
|
+
@db.define()
|
|
618
|
+
class Supplier(TypedTable):
|
|
619
|
+
gid = TypedField(str)
|
|
620
|
+
name = TypedField(str)
|
|
621
|
+
|
|
622
|
+
@db.define()
|
|
623
|
+
class Product(TypedTable):
|
|
624
|
+
name = TypedField(str)
|
|
625
|
+
method: Method
|
|
626
|
+
supplier: Supplier
|
|
627
|
+
|
|
628
|
+
supplier = Supplier.insert(gid="supplier", name="Supplier")
|
|
629
|
+
slow = Method.insert(gid="slow", name="Slow")
|
|
630
|
+
fast = Method.insert(gid="fast", name="Fast")
|
|
631
|
+
Product.insert(name="Product slow", method=slow, supplier=supplier)
|
|
632
|
+
Product.insert(name="Product fast", method=fast, supplier=supplier)
|
|
633
|
+
db.commit()
|
|
634
|
+
|
|
635
|
+
builder = Product.join("method", method="inner").join("supplier", method="inner")
|
|
636
|
+
method_relation = builder.relationships["method"]
|
|
637
|
+
method_alias = method_relation.get_table(db).with_alias(f"method_{hash(method_relation)}")
|
|
638
|
+
builder = builder.orderby(method_alias.name)
|
|
639
|
+
builder = builder.select(Method.gid, Method.name)
|
|
640
|
+
builder = builder.select(Supplier.gid, Supplier.name)
|
|
641
|
+
|
|
642
|
+
page = builder.paginate(page=1, limit=1)
|
|
643
|
+
|
|
644
|
+
assert len(page) == 1
|
|
645
|
+
assert page.first().method.name == "Fast"
|
|
646
|
+
|
|
647
|
+
|
|
605
648
|
def test_execute():
|
|
606
649
|
_setup_data()
|
|
607
650
|
|
|
@@ -888,6 +888,33 @@ def test_join_with_select():
|
|
|
888
888
|
assert not hasattr(user.articles[0], "title")
|
|
889
889
|
|
|
890
890
|
|
|
891
|
+
def test_paginate_with_inner_join_uses_root_entities():
|
|
892
|
+
_setup_data()
|
|
893
|
+
|
|
894
|
+
page1 = User.join("articles", method="inner").orderby(~User.id).paginate(limit=2, page=1)
|
|
895
|
+
page2 = page1.next()
|
|
896
|
+
page1_again = page2.previous()
|
|
897
|
+
|
|
898
|
+
assert [user.id for user in page1] == [4, 3]
|
|
899
|
+
assert [user.id for user in page2] == [2]
|
|
900
|
+
assert [user.id for user in page1_again] == [4, 3]
|
|
901
|
+
|
|
902
|
+
assert sorted(article.title for article in page1.first().articles) == [
|
|
903
|
+
"Untagged Article 1",
|
|
904
|
+
"Untagged Article 2",
|
|
905
|
+
]
|
|
906
|
+
assert [article.title for article in page2.first().articles] == ["Article 1"]
|
|
907
|
+
|
|
908
|
+
assert page1.pagination["total_items"] == 3
|
|
909
|
+
assert page1.pagination["total_pages"] == 2
|
|
910
|
+
assert page1.pagination["has_next_page"] is True
|
|
911
|
+
assert page2.pagination["has_next_page"] is False
|
|
912
|
+
assert page2.pagination["has_prev_page"] is True
|
|
913
|
+
|
|
914
|
+
with pytest.raises(StopIteration):
|
|
915
|
+
page2.next()
|
|
916
|
+
|
|
917
|
+
|
|
891
918
|
def test_count_with_join():
|
|
892
919
|
_setup_data()
|
|
893
920
|
|
|
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
|
|
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
|