diff --git a/frappe/__init__.py b/frappe/__init__.py index b4728f9ac3..6d79cbd760 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -28,7 +28,7 @@ from .exceptions import * from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader) from .utils.lazy_loader import lazy_import -from frappe.query_builder import get_query_builder +from frappe.query_builder import get_query_builder, patch_query_execute # Lazy imports faker = lazy_import('faker') @@ -208,6 +208,7 @@ def init(site, sites_path=None, new_site=False): local.qb = get_query_builder(local.conf.db_type or "mariadb") setup_module_map() + patch_query_execute() local.initialised = True diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index c17ae583ed..2ee3b46b7c 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -145,10 +145,9 @@ def build_table_count_cache(): table_rows = frappe.qb.Field("table_rows").as_("count") information_schema = frappe.qb.Schema("information_schema") - query = frappe.qb.from_(information_schema.tables).select(table_name, table_rows) - - data = frappe.db.sql(query, as_dict=1) - + data = ( + frappe.qb.from_(information_schema.tables).select(table_name, table_rows) + ).run(as_dict=True) counts = {d.get('name').lstrip('tab'): d.get('count', None) for d in data} _cache.set_value("information_schema:counts", counts) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index e7f0f1a763..aa441b7d71 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -43,9 +43,13 @@ def get_all_empty_tables_by_module(): table_name = frappe.qb.Field("table_name") information_schema = frappe.qb.Schema("information_schema") - query = frappe.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0) + empty_tables = ( + frappe.qb.from_(information_schema.tables) + .select(table_name) + .where(table_rows == 0) + ).run() - empty_tables = {r[0] for r in frappe.db.sql(query)} + empty_tables = {r[0] for r in empty_tables} results = frappe.get_all("DocType", fields=["name", "module"]) empty_tables_by_module = {} diff --git a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py index 79bacef5a2..5aaadd00e8 100644 --- a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py +++ b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py @@ -7,16 +7,14 @@ def execute(): ToDo = frappe.qb.DocType("ToDo") assignees = GroupConcat("owner").distinct().as_("assignees") - query = ( + assignments = ( frappe.qb.from_(ToDo) .select(ToDo.name, ToDo.reference_type, assignees) .where(Coalesce(ToDo.reference_type, "") != "") .where(Coalesce(ToDo.reference_name, "") != "") .where(ToDo.status != "Cancelled") .groupby(ToDo.reference_type, ToDo.reference_name) - ) - - assignments = frappe.db.sql(query, as_dict=True) + ).run(as_dict=True) for doc in assignments: assignments = doc.assignees.split(",") diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index 798c34b6cc..f1503c88b8 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -1 +1 @@ -from frappe.query_builder.utils import get_query_builder +from frappe.query_builder.utils import get_query_builder, patch_query_execute diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py index b52a3606e8..67e2c392f3 100644 --- a/frappe/query_builder/utils.py +++ b/frappe/query_builder/utils.py @@ -1,5 +1,6 @@ from enum import Enum -from typing import Any, Callable, Dict +from typing import Any, Callable, Dict, get_type_hints +from importlib import import_module from pypika import Query @@ -19,6 +20,9 @@ class ImportMapper: db = db_type_is(frappe.conf.db_type or "mariadb") return self.func_map[db](*args, **kwds) +class BuilderIdentificationFailed(Exception): + def __init__(self): + super().__init__("Couldn't guess builder") def get_query_builder(type_of_db: str) -> Query: """[return the query builder object] @@ -32,3 +36,25 @@ def get_query_builder(type_of_db: str) -> Query: db = db_type_is(type_of_db) picks = {db_type_is.MARIADB: MariaDB, db_type_is.POSTGRES: Postgres} return picks[db] + +def get_attr(method_string): + modulename = '.'.join(method_string.split('.')[:-1]) + methodname = method_string.split('.')[-1] + return getattr(import_module(modulename), methodname) + +def patch_query_execute(): + """Patch the Query Builder with helper execute method + This excludes the use of `frappe.db.sql` method while + executing the query object + """ + + def execute_query(query, **kwargs): + return frappe.db.sql(query, **kwargs) + + query_class = get_attr(str(frappe.qb).split("'")[1]) + builder_class = get_type_hints(query_class._builder).get('return') + + if not builder_class: + raise BuilderIdentificationFailed + + builder_class.run = execute_query diff --git a/frappe/tests/test_query_builder.py b/frappe/tests/test_query_builder.py index c5ad457b2a..7a0935a63b 100644 --- a/frappe/tests/test_query_builder.py +++ b/frappe/tests/test_query_builder.py @@ -42,6 +42,12 @@ class TestBuilderBase(object): self.assertEqual("__Auth", frappe.qb.DocType("__Auth").get_sql()) self.assertEqual("Notes", frappe.qb.Table("Notes").get_sql()) + def test_run_patcher(self): + query = frappe.qb.from_("ToDo").select("*").limit(1) + data = query.run(as_dict=True) + self.assertTrue("run" in dir(query)) + self.assertIsInstance(query.run, Callable) + self.assertIsInstance(data, list) @run_only_if(db_type_is.MARIADB) class TestBuilderMaria(unittest.TestCase, TestBuilderBase): diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index e58d6097c8..be941abd51 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -445,7 +445,7 @@ def search(text, start=0, limit=20, doctype=""): if cint(start) > 0: query = query.offset(start) - result = frappe.db.sql(query, as_dict=True) + result = query.run(as_dict=True) results.extend(result) diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py index 99fce02f1e..b4e76ba9fd 100644 --- a/frappe/website/report/website_analytics/website_analytics.py +++ b/frappe/website/report/website_analytics/website_analytics.py @@ -64,7 +64,7 @@ class WebsiteAnalytics(object): case = frappe.qb.terms.Case().when(WebPageView.is_unique == "1", "1") count_is_unique = Count(case).as_("unique_count") - query = ( + return ( frappe.qb.from_(WebPageView) .select("path", count_all, count_is_unique) .where( @@ -72,8 +72,7 @@ class WebsiteAnalytics(object): ) .groupby(WebPageView.path) .orderby("count", Order=frappe.qb.desc) - ) - return frappe.db.sql(query) + ).run() def _get_query_for_mariadb(self): filters_range = self.filters.range