Skip to content

Conversation

theShinigami
Copy link

@theShinigami theShinigami commented Sep 25, 2025

Description

Added a new context variable allow_ddt_models_tracking to control whether SQLPanel tracks DDT queries.

  • Added allow_ddt_models_tracking contextvar, defaulting to False
  • SQLPanel now checks this variable to decide if DDT models queries should be tracked.

Fixes #2165

Checklist:

  • I have added the relevant tests for this change.
  • I have added an item to the Pending section of docs/changes.rst.

if not allow_ddt_models_tracking.get() and any(
table in sql for table in DDT_MODELS
):
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely shouldn't be returning inside a finally block.

Starting in python 3.14 the compiler emits a SyntaxWarning when seeing this construct:
https://docs.python.org/3.14/reference/compound_stmts.html#finally

Copy link
Author

@theShinigami theShinigami Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed it when ruff complained 😆, I'm making changes. Thank you for the reference. 🙂

@theShinigami theShinigami changed the title Add SQLPanel support for DDT model query tracking Add support for enabling/disabling SQLPanel tracking of DDT model queries Sep 25, 2025
Comment on lines 32 to 34
allow_ddt_models_tracking = contextvars.ContextVar(
"debug-toolbar-allow-ddt-models", default=False
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theShinigami I don't think I've communicated this idea well. The purpose here is to have a value that enables and disables whether we're using the sql panel. So by default it should track all SQL. However, when we get to the toolbar's own processing, it should be disabled. That way the toolbar isn't monitoring itself. Does that make sense?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tim-schilling I'm not quite getting the idea, could you elaborate a little more? 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this context var:

is_recording = contextvars.ContextVar(
    "debug-toolbar-is-recording", default=True
)

Think of the is_recording as a global variable that controls whether a panel actually records the activity of the django project. When it's True, the SQL panel should record SQL queries. When it's False, the SQL panel shouldn't record SQL queries.

This comes into play for this issue because we want to disable the SQL panel when the toolbar is processing the request, but not when everything else is.

So we need to identify when to set and when to unset it (somewhere in the middleware most likely, we may run into some weirdness with the async flow), then lastly update the SQL panel to avoid tracking when it's not supposed to.

The places that you'll likely want to update are:

This relies on the idea that record_stats (what uses the HistoryEntry model) will only ever be called from generate_stats. I think that will work...

Copy link
Member

@tim-schilling tim-schilling Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like I may be missing something here around enable_instrumentation and disable_instrumentation. This global variable shouldn't be necessary. Sorry, my thoughts aren't fully baked yet

Comment on lines 39 to 40
if use_iterator:
qs = qs.iterator()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this isn't used, so we should be able to remove it and the one from async_sql_call_ddt

return list(qs)


def sql_call_ddt(*, use_iterator=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a doc string here to explain what this function is for please? I think that'll help us in the future when reviewing code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, posted too soon. I think a doc string on these two helper functions and the four tests would be fantastic.

Comment on lines 130 to 134
query = self.panel._queries[0]
self.assertEqual(query["alias"], "default")
self.assertTrue("sql" in query)
self.assertTrue("duration" in query)
self.assertTrue("stacktrace" in query)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may be better off replacing this with a check on the models' table in the sql query rather than these assertions.

# ensure the stacktrace is populated
self.assertTrue(len(query["stacktrace"]) > 0)

def test_ddt_models_untracking(self):
Copy link
Member

@tim-schilling tim-schilling Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_ddt_models_untracking(self):
def test_ddt_models_not_tracked(self):

nit: Small naming improvement (should be used on the other similar test too)

self.assertTrue(len(query["stacktrace"]) > 0)

@override_settings(DEBUG_TOOLBAR_CONFIG={"TRACK_DDT_MODELS": True})
def test_ddt_models_tracking(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_ddt_models_tracking(self):
def test_ddt_models_tracked(self):

nit: Small naming improvement (should be used on the other similar test too)

@tim-schilling
Copy link
Member

I think I can live with the SQL search. I don't like it because anything can match on it, but I'm not sure how to exactly improve it. @matthiask any thoughts?

@tim-schilling
Copy link
Member

To clarify, a query such as `select * from my_other_table WHERE my_column = 'debug_toolbar.historyentry'; would match. For any possible query that a Django application is running, we're looking for a specific string in it. There's a possibility of false positives (matches that we don't actually want to match on)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SQLPanel is tracking the DatabaseStore queries
3 participants