Skip to content

Commit 0175bec

Browse files
committed
Address review feedback
1 parent 048534f commit 0175bec

File tree

11 files changed

+33
-83
lines changed

11 files changed

+33
-83
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ DEBUG_TOOLBAR_PANELS = [
6767
```python
6868
# Maximum number of documents to return when re-executing select
6969
# queries (default is 100).
70-
DJDT_MQL_MAX_SELECT_RESULTS = 25
70+
DJDT_MQL_MAX_QUERY_RESULTS = 25
7171

7272
# Queries slower than this threshold (in milliseconds) are highlighted
7373
# in the debug toolbar (default is 500 ms).
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from django_mongodb_extensions.mql_panel.panel import MQLPanel
22

3-
__all__ = ["MQLPanel"]
3+
__all__ = [MQLPanel.panel_id]

django_mongodb_extensions/mql_panel/forms.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
from pymongo import errors as pymongo_errors
1111

1212
from django_mongodb_extensions.mql_panel.utils import (
13-
MQL_PANEL_ID,
1413
QueryParts,
15-
get_max_select_results,
14+
get_max_query_results,
1615
parse_query_args,
1716
)
1817

1918

2019
class MQLBaseForm(SQLSelectForm):
2120
def clean(self):
21+
from .panel import MQLPanel
22+
2223
# Call forms.Form.clean() to bypass SQLSelectForm.clean() which has
2324
# SQL-specific validation
2425
cleaned_data = forms.Form.clean(self)
@@ -28,10 +29,10 @@ def clean(self):
2829
djdt_query_id = cleaned_data.get("djdt_query_id")
2930
if not djdt_query_id:
3031
raise ValidationError(_("Missing query ID."))
31-
toolbar = DebugToolbar.fetch(request_id, panel_id=MQL_PANEL_ID)
32+
toolbar = DebugToolbar.fetch(request_id, panel_id=MQLPanel.panel_id)
3233
if toolbar is None:
3334
raise ValidationError(_("Data for this panel isn't available anymore."))
34-
panel = toolbar.get_panel_by_id(MQL_PANEL_ID)
35+
panel = toolbar.get_panel_by_id(MQLPanel.panel_id)
3536
stats = panel.get_stats()
3637
if not stats or "queries" not in stats:
3738
raise ValidationError(_("Query data is not available."))
@@ -107,7 +108,7 @@ def _handle_operation_error(self, error, mql_string, operation_type="operation")
107108
if isinstance(error, ValueError):
108109
header = "Query Parsing Error"
109110
messages = [f"Query parsing error: {error}"]
110-
if operation_type == "select":
111+
if operation_type == "query":
111112
messages += [
112113
"The MQL panel can only re-execute read operations.",
113114
"Write operations (insert, update, delete) cannot be re-executed.",
@@ -177,23 +178,23 @@ class MQLQueryForm(MQLBaseForm):
177178
def _execute_aggregate(self, collection, args_list):
178179
pipeline = args_list[0] if args_list else []
179180
result_docs = []
180-
max_results = get_max_select_results()
181+
max_results = get_max_query_results()
181182
with collection.aggregate(pipeline) as cursor:
182183
for i, doc in enumerate(cursor):
183184
if i >= max_results:
184185
break
185186
result_docs.append(doc)
186187
return result_docs
187188

188-
def _execute_select(self, db, collection, collection_name, operation, args_list):
189+
def _execute_query(self, db, collection, collection_name, operation, args_list):
189190
if operation == "aggregate":
190191
result_docs = self._execute_aggregate(collection, args_list)
191192
else:
192193
raise ValueError(f"Unsupported read operation: {operation}")
193194
return self.convert_documents_to_table(result_docs)
194195

195-
def select(self):
196-
return self._execute_operation("select", self._execute_select)
196+
def query(self):
197+
return self._execute_operation("query", self._execute_query)
197198

198199
def _format_cell_value(self, value):
199200
"""Format a single cell value for table display."""

django_mongodb_extensions/mql_panel/panel.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
from django_mongodb_extensions.mql_panel import views
1818
from django_mongodb_extensions.mql_panel.utils import (
19-
MQL_PANEL_ID,
20-
MQL_READ_OPERATIONS,
2119
get_mql_warning_threshold,
2220
patch_get_collection,
2321
patch_new_connection,
@@ -30,7 +28,8 @@
3028

3129

3230
class MQLPanel(SQLPanel):
33-
panel_id = MQL_PANEL_ID
31+
nav_title = _("MQL")
32+
template = "mql_panel/mql.html"
3433

3534
def __init__(self, *args, **kwargs):
3635
super().__init__(*args, **kwargs)
@@ -52,10 +51,6 @@ def record(self, **kwargs):
5251
self._databases[alias]["num_queries"] += 1
5352
self._mql_time += kwargs["duration"]
5453

55-
# Implement Panel API
56-
nav_title = _("MQL")
57-
template = "mql_panel/mql.html"
58-
5954
@classmethod
6055
def get_urls(cls):
6156
return [
@@ -68,8 +63,8 @@ def nav_subtitle(self):
6863
stats = self.get_stats()
6964
query_count = len(stats.get("queries", []))
7065
return ngettext(
71-
"%(query_count)d query in %(mql_time).2fms",
72-
"%(query_count)d queries in %(mql_time).2fms",
66+
"%(query_count)d query in %(mql_time).3f ms",
67+
"%(query_count)d queries in %(mql_time).3f ms",
7368
query_count,
7469
) % {
7570
"query_count": query_count,
@@ -120,7 +115,7 @@ def _hex_to_rgb(hex_color):
120115

121116
@staticmethod
122117
def _is_read_operation(operation):
123-
return operation in MQL_READ_OPERATIONS
118+
return operation in {"aggregate"}
124119

125120
def generate_stats(self, request, response):
126121
duplicate_query_groups = defaultdict(list)
@@ -137,8 +132,9 @@ def generate_stats(self, request, response):
137132
duplicate_query_groups[(alias, dup_key)].append(query)
138133
query["is_slow"] = query["duration"] > mql_warning_threshold
139134
operation = query.get("mql_operation", "")
140-
# Only show Sel/Expl buttons if it's a read operation AND
141-
# the args were successfully serialized (mql_args_json is not None).
135+
# Only show Query/Explain buttons if it's a read operation and
136+
# the args were successfully serialized (mql_args_json is not
137+
# None).
142138
args_json = query.get("mql_args_json")
143139
query["is_query"] = (
144140
self._is_read_operation(operation) and args_json is not None

django_mongodb_extensions/mql_panel/utils.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
from django.conf import settings
77
from django_mongodb_backend.utils import OperationDebugWrapper
88

9-
MQL_PANEL_ID = "MQLPanel"
10-
MQL_READ_OPERATIONS = {"aggregate"} # noqa
11-
DEFAULT_MAX_SELECT_RESULTS = 100
9+
DEFAULT_MAX_QUERY_RESULTS = 100
1210
DEFAULT_MQL_WARNING_THRESHOLD = 500
1311
_patched_connections = weakref.WeakSet()
1412

@@ -50,10 +48,7 @@ def __init__(self, db, collection, logger):
5048
def log(self, op, duration, args, kwargs=None):
5149
args_str = ", ".join(repr(arg) for arg in args)
5250
operation = f"db.{self.collection_name}{op}({args_str})"
53-
try:
54-
args_json = json_util.dumps(list(args))
55-
except (TypeError, ValueError):
56-
args_json = None
51+
args_json = json_util.dumps(list(args))
5752
if self.logger:
5853
self.logger.record(
5954
alias=self.db.alias,
@@ -84,13 +79,13 @@ def format_mql_query(query):
8479
return f"db.{collection_name}.{operation}(\n{args_formatted}\n)"
8580

8681

87-
def get_max_select_results():
82+
def get_max_query_results():
8883
"""Get the maximum number of results to return when re-executing queries.
8984
90-
Return the value from Django settings DJDT_MQL_MAX_SELECT_RESULTS if set,
85+
Return the value from Django settings DJDT_MQL_MAX_QUERY_RESULTS if set,
9186
otherwise return the default value.
9287
"""
93-
return getattr(settings, "DJDT_MQL_MAX_SELECT_RESULTS", DEFAULT_MAX_SELECT_RESULTS)
88+
return getattr(settings, "DJDT_MQL_MAX_QUERY_RESULTS", DEFAULT_MAX_QUERY_RESULTS)
9489

9590

9691
def get_mql_warning_threshold():
@@ -109,16 +104,7 @@ def parse_query_args(query_dict):
109104
collection_name = query_dict["mql_collection"]
110105
operation = query_dict["mql_operation"]
111106
args_json = query_dict["mql_args_json"]
112-
# If args_json is None, serialization failed when the query was logged.
113-
# Treat this as unreplayable to avoid re-executing a different query.
114-
if args_json is None:
115-
raise ValueError(
116-
"Query arguments could not be serialized when logged. "
117-
"This query cannot be re-executed because the original arguments are unavailable."
118-
)
119-
if args_json != "":
120-
return collection_name, operation, json_util.loads(args_json)
121-
return collection_name, operation, []
107+
return collection_name, operation, json_util.loads(args_json)
122108

123109

124110
def patch_get_collection(connection):

django_mongodb_extensions/mql_panel/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def mql_query(request):
4444
form = MQLQueryForm(verified_data)
4545
if form.is_valid():
4646
query = form.cleaned_data["query"]
47-
result, headers = form.select()
47+
result, headers = form.query()
4848

4949
context = {
5050
"result": result,

django_mongodb_extensions/templates/mql_panel/mql.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{% for alias, info in databases %}
44
<li>
55
<strong><span class="djdt-color" data-djdt-styles="backgroundColor:rgb({{ info.rgb_color|join:', ' }})"></span> {{ alias }}</strong>
6-
{{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %}
6+
{{ info.time_spent|floatformat:"3" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %}
77
{% if info.duplicate_count %}
88
{% blocktrans with dupes=info.duplicate_count trimmed %}
99
including <abbr title="Duplicate queries execute the same MQL operation and parameters.">{{ dupes }} duplicates</abbr>
@@ -40,7 +40,7 @@
4040
<button type="button" class="djToggleSwitch" data-toggle-name="sqlMain" data-toggle-id="{{ forloop.counter }}">+</button>
4141
</td>
4242
<td>
43-
<div class="djDebugSql">{{ query.mql|safe }}</div>
43+
<div class="djDebugSql">{{ query.mql }}</div>
4444
{% if query.duplicate_count %}
4545
<strong>
4646
<span class="djdt-color" data-djdt-styles="backgroundColor:{{ query.duplicate_color }}"></span>
@@ -60,7 +60,7 @@
6060
<form method="post">
6161
{{ query.form.as_div }}
6262
<button formaction="{% url 'djdt:mql_query' %}" class="remoteCall">Query</button>
63-
<button formaction="{% url 'djdt:mql_explain' %}" class="remoteCall">Expl</button>
63+
<button formaction="{% url 'djdt:mql_explain' %}" class="remoteCall">Explain</button>
6464
</form>
6565
{% endif %}
6666
{% endif %}

django_mongodb_extensions/templates/mql_panel/mql_query.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ <h3>{% translate "Query Results" %}</h3>
1313
<dt>{% translate "Database" %}</dt>
1414
<dd>{{ alias }}</dd>
1515
</dl>
16+
<h4>{% translate "Query Results" %}</h4>
1617
{% if result %}
17-
<h4>{% translate "Query Results" %}</h4>
1818
<table>
1919
<thead>
2020
<tr>

pyproject.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,3 @@ test = [
1818

1919
[tool.ruff.lint]
2020
select = ["I", "F"]
21-
22-
[tool.hatch.build.targets.wheel]
23-
packages = ["django_mongodb_extensions"]

tests/mql_panel/test_panel.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from django_mongodb_extensions.mql_panel import MQLPanel
77
from django_mongodb_extensions.mql_panel.forms import MQLQueryForm
8-
from django_mongodb_extensions.mql_panel.utils import parse_query_args
98

109
rf = RequestFactory()
1110

@@ -15,7 +14,7 @@ def mql_call():
1514
return list(User.objects.all())
1615

1716

18-
class BaseMQLTestCase(TestCase):
17+
class MQLPanelTests(TestCase):
1918
panel_id = MQLPanel.panel_id
2019

2120
def setUp(self):
@@ -32,8 +31,6 @@ def tearDown(self):
3231
def get_response(self, request):
3332
return self._get_response(request)
3433

35-
36-
class MQLPanelTests(BaseMQLTestCase):
3734
def test_disabled(self):
3835
config = {"DISABLE_PANELS": {"django_mongodb_extensions.mql_panel.MQLPanel"}}
3936
self.assertIs(self.panel.enabled, True)
@@ -103,7 +100,7 @@ def test_nav_subtitle(self):
103100
mql_call()
104101
response = self.panel.process_request(self.request)
105102
self.panel.generate_stats(self.request, response)
106-
self.assertRegex(self.panel.nav_subtitle, r"1 query in \d+\.\d+ms")
103+
self.assertRegex(self.panel.nav_subtitle, r"1 query in \d+\.\d+ ms")
107104

108105
def test_title(self):
109106
mql_call()
@@ -171,16 +168,3 @@ def test_handle_operation_error_format(self):
171168

172169
# Should have one header
173170
self.assertEqual(headers[0], "Query Parsing Error")
174-
175-
176-
class ParseQueryArgsTests(TestCase):
177-
def test_unserializable_args(self):
178-
"""None mql_args_json raises ValueError to prevent replaying a different query."""
179-
with self.assertRaisesMessage(ValueError, "could not be serialized"):
180-
parse_query_args(
181-
{
182-
"mql_collection": "auth_user",
183-
"mql_operation": "aggregate",
184-
"mql_args_json": None,
185-
}
186-
)

0 commit comments

Comments
 (0)