diff --git a/debug_toolbar/panels/sql/panel.py b/debug_toolbar/panels/sql/panel.py index c8576e16f..984a2074a 100644 --- a/debug_toolbar/panels/sql/panel.py +++ b/debug_toolbar/panels/sql/panel.py @@ -6,6 +6,7 @@ from django.urls import path from django.utils.translation import gettext_lazy as _, ngettext +from debug_toolbar import settings as dt_settings from debug_toolbar.forms import SignedDataForm from debug_toolbar.panels import Panel from debug_toolbar.panels.sql import views @@ -110,9 +111,7 @@ class SQLPanel(Panel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._offset = {k: len(connections[k].queries) for k in connections} self._sql_time = 0 - self._num_queries = 0 self._queries = [] self._databases = {} # synthetic transaction IDs, keyed by DB alias @@ -151,7 +150,6 @@ def record(self, **kwargs): self._databases[alias]["time_spent"] += kwargs["duration"] self._databases[alias]["num_queries"] += 1 self._sql_time += kwargs["duration"] - self._num_queries += 1 # Implement the Panel API @@ -159,12 +157,13 @@ def record(self, **kwargs): @property def nav_subtitle(self): + query_count = len(self._queries) return ngettext( "%(query_count)d query in %(sql_time).2fms", "%(query_count)d queries in %(sql_time).2fms", - self._num_queries, + query_count, ) % { - "query_count": self._num_queries, + "query_count": query_count, "sql_time": self._sql_time, } @@ -204,6 +203,8 @@ def generate_stats(self, request, response): duplicate_query_groups = defaultdict(list) if self._queries: + sql_warning_threshold = dt_settings.get_config()["SQL_WARNING_THRESHOLD"] + width_ratio_tally = 0 factor = int(256.0 / (len(self._databases) * 2.5)) for n, db in enumerate(self._databases.values()): @@ -261,6 +262,12 @@ def generate_stats(self, request, response): if query["sql"]: query["sql"] = reformat_sql(query["sql"], with_toggle=True) + + query["is_slow"] = query["duration"] > sql_warning_threshold + query["is_select"] = ( + query["raw_sql"].lower().lstrip().startswith("select") + ) + query["rgb_color"] = self._databases[alias]["rgb_color"] try: query["width_ratio"] = (query["duration"] / self._sql_time) * 100 diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index a85ac51ad..ee75f8e06 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -7,7 +7,6 @@ from django.db.backends.utils import CursorWrapper from django.utils.encoding import force_str -from debug_toolbar import settings as dt_settings from debug_toolbar.utils import get_stack_trace, get_template_info try: @@ -34,8 +33,14 @@ class SQLQueryTriggered(Exception): def wrap_cursor(connection): - # If running a Django SimpleTestCase, which isn't allowed to access the database, - # don't perform any monkey patching. + # When running a SimpleTestCase, Django monkey patches some DatabaseWrapper + # methods, including .cursor() and .chunked_cursor(), to raise an exception + # if the test code tries to access the database, and then undoes the monkey + # patching when the test case is finished. If we monkey patch those methods + # also, Django's process of undoing those monkey patches will fail. To + # avoid this failure, and because database access is not allowed during a + # SimpleTextCase anyway, skip applying our instrumentation monkey patches if + # we detect that Django has already monkey patched DatabaseWrapper.cursor(). if isinstance(connection.cursor, django.test.testcases._DatabaseFailure): return if not hasattr(connection, "_djdt_cursor"): @@ -182,7 +187,7 @@ def _record(self, method, sql, params): else: sql = str(sql) - params = { + kwargs = { "vendor": vendor, "alias": alias, "sql": self._last_executed_query(sql, params), @@ -191,12 +196,6 @@ def _record(self, method, sql, params): "params": _params, "raw_params": params, "stacktrace": get_stack_trace(skip=2), - "start_time": start_time, - "stop_time": stop_time, - "is_slow": ( - duration > dt_settings.get_config()["SQL_WARNING_THRESHOLD"] - ), - "is_select": sql.lower().strip().startswith("select"), "template_info": template_info, } @@ -228,7 +227,7 @@ def _record(self, method, sql, params): else: trans_id = None - params.update( + kwargs.update( { "trans_id": trans_id, "trans_status": conn.info.transaction_status, @@ -237,7 +236,7 @@ def _record(self, method, sql, params): ) # We keep `sql` to maintain backwards compatibility - self.logger.record(**params) + self.logger.record(**kwargs) def callproc(self, procname, params=None): return self._record(super().callproc, procname, params)