From c90d3c5c20d179c2e4313d77625791b79b9aff9d Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 18 Dec 2025 14:57:27 +0530 Subject: [PATCH 1/4] refactor(reportview): use query builder Signed-off-by: Akhil Narang --- frappe/desk/reportview.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index c779277b9e..77be1f83ea 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -14,7 +14,7 @@ from frappe import _ from frappe.core.doctype.access_log.access_log import make_access_log from frappe.model import child_table_fields, default_fields, get_permitted_fields, optional_fields from frappe.model.base_document import get_controller -from frappe.model.db_query import DatabaseQuery +from frappe.model.qb_query import DatabaseQuery from frappe.model.utils import is_virtual_doctype from frappe.utils import add_user_info, cint, format_duration from frappe.utils.data import sbool @@ -60,16 +60,14 @@ def get_count() -> int | None: return frappe.call(controller.get_count, args=args, **args) args.distinct = sbool(args.distinct) - distinct = "distinct " if args.distinct else "" args.limit = cint(args.limit) - fieldname = f"{distinct}`tab{args.doctype}`.name" - args.pop("distinct") # to avoid a double DISTINCT concat in db_query + fieldname = f"`tab{args.doctype}`.name" args.order_by = None # args.limit is specified to avoid getting accurate count. if not args.limit: args.fields = [fieldname] - partial_query = execute(**args, run=0) + partial_query = execute(**args, run=0).get_sql() return frappe.db.sql(f"select count(*) from ( {partial_query} ) p")[0][0] args.fields = [fieldname] @@ -192,9 +190,8 @@ def setup_group_by(data): frappe.throw(_("Invalid aggregate function")) if frappe.db.has_column(data.aggregate_on_doctype, data.aggregate_on_field): - data.fields.append( - f"{data.aggregate_function}(`tab{data.aggregate_on_doctype}`.`{data.aggregate_on_field}`) AS _aggregate_column" - ) + field = f"`tab{data.aggregate_on_doctype}`.`{data.aggregate_on_field}`" + data.fields.append({data.aggregate_function.upper(): field, "as": "_aggregate_column"}) else: raise_invalid_field(data.aggregate_on_field) @@ -797,6 +794,8 @@ def scrub_user_tags(tagcount): # used in building query in queries.py def get_match_cond(doctype, as_condition=True): + from frappe.model.db_query import DatabaseQuery + cond = DatabaseQuery(doctype).build_match_conditions(as_condition=as_condition) if not as_condition: return cond @@ -805,6 +804,8 @@ def get_match_cond(doctype, as_condition=True): def build_match_conditions(doctype, user=None, as_condition=True): + from frappe.model.db_query import DatabaseQuery + match_conditions = DatabaseQuery(doctype, user=user).build_match_conditions(as_condition=as_condition) if as_condition: return match_conditions.replace("%", "%%") @@ -841,6 +842,8 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with else: flt.append([doctype, f[0], "=", f[1]]) + from frappe.model.db_query import DatabaseQuery + query = DatabaseQuery(doctype) query.filters = flt query.conditions = conditions From 81f7a247f0276baee74d56bca30443f6f537c4ab Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 18 Dec 2025 16:33:37 +0530 Subject: [PATCH 2/4] fix(query): track aliases for `DynamicTableField` as well Child tables, links for example. Signed-off-by: Akhil Narang --- frappe/database/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/query.py b/frappe/database/query.py index 37f275da46..3a445c5760 100644 --- a/frappe/database/query.py +++ b/frappe/database/query.py @@ -346,7 +346,7 @@ class Engine: # Track field aliases for use in group_by/order_by for field in self.fields: - if isinstance(field, Field) and field.alias: + if isinstance(field, Field | DynamicTableField) and field.alias: self.field_aliases.add(field.alias) if self.apply_permissions: From c0ebc96597d521649d8345b0c131da97e7930302 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 18 Dec 2025 16:35:14 +0530 Subject: [PATCH 3/4] feat(qb_query): expose `fields` like db_query Signed-off-by: Akhil Narang --- frappe/model/qb_query.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/model/qb_query.py b/frappe/model/qb_query.py index d4fb1d9f06..d6785167c1 100644 --- a/frappe/model/qb_query.py +++ b/frappe/model/qb_query.py @@ -117,6 +117,8 @@ class DatabaseQuery: if not fields: fields = [pluck or "name"] + self.fields = fields + # Handle virtual doctypes before any other processing if is_virtual_doctype(self.doctype): return self._handle_virtual_doctype( From 3a2dcaa2deef8f84ca3c71abb452e3ef60c0823f Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 18 Dec 2025 18:11:02 +0530 Subject: [PATCH 4/4] fix(with_comment_count): handle string values Signed-off-by: Akhil Narang --- frappe/model/qb_query.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/model/qb_query.py b/frappe/model/qb_query.py index d6785167c1..004d44ff94 100644 --- a/frappe/model/qb_query.py +++ b/frappe/model/qb_query.py @@ -12,6 +12,7 @@ from frappe.deprecation_dumpster import deprecation_warning from frappe.model.utils import is_virtual_doctype from frappe.model.utils.user_settings import get_user_settings, update_user_settings from frappe.query_builder.utils import Column +from frappe.utils import sbool class DatabaseQuery: @@ -211,7 +212,7 @@ class DatabaseQuery: result = query.run(debug=debug, as_dict=not as_list, update=update) # Add comment count if requested and not as_list - if with_comment_count and not as_list and self.doctype: + if sbool(with_comment_count) and not as_list and self.doctype: self._add_comment_count(result) # Save user settings if requested