Skip to content

Commit 9528b1d

Browse files
committed
Address review feedback
1 parent 66d97ac commit 9528b1d

File tree

7 files changed

+114
-49
lines changed

7 files changed

+114
-49
lines changed

django_mongodb_extensions/mql_panel/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def _execute_query(self, db, collection, collection_name, operation, args_list):
110110

111111
def _flatten_single_key_dicts(self, obj):
112112
if isinstance(obj, dict):
113-
if len(obj) == 1:
113+
if len(obj) == 1 and next(iter(obj)).startswith("$"):
114114
only_value = next(iter(obj.values()))
115115
return self._flatten_single_key_dicts(only_value)
116116
return {

django_mongodb_extensions/mql_panel/panel.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,20 @@ def content(self):
8181

8282
def disable_instrumentation(self):
8383
for connection in connections.all():
84-
if hasattr(connection, "_djdt_logger"):
85-
connection._djdt_logger = None
84+
if hasattr(connection, "_mql_djdt_logger"):
85+
connection._mql_djdt_logger = None
8686

8787
def enable_instrumentation(self):
8888
# Only patch MongoDB connections (those with get_collection method).
8989
# This allows the panel to work in multi-database setups with
9090
# both MongoDB and relational databases.
91+
#
92+
# Use _mql_djdt_logger (not _djdt_logger) to avoid conflicting with the
93+
# SQL panel, which sets _djdt_logger on all connections regardless of type.
9194
for connection in connections.all():
9295
if hasattr(connection, "get_collection"):
9396
patch_get_collection(connection)
94-
connection._djdt_logger = self
97+
connection._mql_djdt_logger = self
9598

9699
def generate_stats(self, request, response):
97100
duplicate_query_groups = defaultdict(list)

django_mongodb_extensions/mql_panel/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ def patch_get_collection(connection):
8585
connection._original_get_collection = connection.get_collection
8686

8787
def get_collection(self, name, **kwargs):
88-
logger = getattr(self, "_djdt_logger", None)
88+
# Use _mql_djdt_logger (not _djdt_logger) to avoid conflicting with the
89+
# SQL panel, which sets _djdt_logger on all connections.
90+
logger = getattr(self, "_mql_djdt_logger", None)
8991
if logger:
9092
collection = self._original_get_collection(name, **kwargs)
9193
return DebugToolbarWrapper(self, collection, logger)

django_mongodb_extensions/templates/mql_panel/mql_query.html

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,7 @@ <h4>{% translate "Query Results" %}</h4>
3434
<tr>
3535
<th>{{ sub.key }}</th>
3636
<td>
37-
{% if sub.type == "dict" %}
38-
<table class="subtable">
39-
{% for inner in sub.value %}
40-
<tr><th>{{ inner.key }}</th><td>{{ inner.value }}</td></tr>
41-
{% endfor %}
42-
</table>
43-
{% elif sub.type == "list" %}
44-
<ul>
45-
{% for inner in sub.value %}
46-
<li>{{ inner.value }}</li>
47-
{% endfor %}
48-
</ul>
49-
{% else %}
50-
{{ sub.value }}
51-
{% endif %}
37+
{% include 'mql_panel/subtype_value.html' %}
5238
</td>
5339
</tr>
5440
{% endfor %}
@@ -57,21 +43,7 @@ <h4>{% translate "Query Results" %}</h4>
5743
<ul>
5844
{% for sub in column.value %}
5945
<li>
60-
{% if sub.type == "dict" %}
61-
<table class="subtable">
62-
{% for inner in sub.value %}
63-
<tr><th>{{ inner.key }}</th><td>{{ inner.value }}</td></tr>
64-
{% endfor %}
65-
</table>
66-
{% elif sub.type == "list" %}
67-
<ul>
68-
{% for inner in sub.value %}
69-
<li>{{ inner.value }}</li>
70-
{% endfor %}
71-
</ul>
72-
{% else %}
73-
{{ sub.value }}
74-
{% endif %}
46+
{% include 'mql_panel/subtype_value.html' %}
7547
</li>
7648
{% endfor %}
7749
</ul>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{% if sub.type == "dict" %}
2+
<table class="subtable">
3+
{% for inner in sub.value %}
4+
<tr>
5+
<th>{{ inner.key }}</th>
6+
<td>
7+
{% if inner.type %}
8+
{% include "mql_panel/subtype_value.html" with sub=inner %}
9+
{% else %}
10+
{{ inner.value }}
11+
{% endif %}
12+
</td>
13+
</tr>
14+
{% endfor %}
15+
</table>
16+
{% elif sub.type == "list" %}
17+
<ul>
18+
{% for inner in sub.value %}
19+
<li>
20+
{% if inner.type %}
21+
{% include "mql_panel/subtype_value.html" with sub=inner %}
22+
{% else %}
23+
{{ inner.value }}
24+
{% endif %}
25+
</li>
26+
{% endfor %}
27+
</ul>
28+
{% else %}
29+
{{ sub.value }}
30+
{% endif %}

tests/mql_panel/test_forms.py

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import datetime
2-
import json
32

4-
from bson import ObjectId, json_util
3+
from bson import ObjectId
54
from django.test import TestCase
65

76
from django_mongodb_extensions.mql_panel.forms import MQLQueryForm
@@ -12,13 +11,11 @@ def setUp(self):
1211
self.form = MQLQueryForm()
1312

1413
def test_empty_documents(self):
15-
"""Empty document list returns empty rows and headers."""
1614
rows, headers = self.form.convert_documents_to_table([])
1715
self.assertEqual(rows, [])
1816
self.assertEqual(headers, [])
1917

2018
def test_simple_fields(self):
21-
"""Primitive field values like in model query output."""
2219
cases = [
2320
("username", {"value": "username", "is_json": False}),
2421
(42, {"value": "42", "is_json": False}),
@@ -30,20 +27,16 @@ def test_simple_fields(self):
3027
self.assertEqual(self.form._format_cell_value(input_value), expected)
3128

3229
def test_objectid(self):
33-
"""MongoDB ObjectId from a document."""
34-
oid = "69cd52da72ad0703d3dfef51"
30+
oid = ObjectId("69cd52da72ad0703d3dfef51")
3531
result = self.form._format_cell_value(oid)
36-
# ObjectIds serialize to {"$oid": "<id>"} which is single-key
3732
self.assertIs(result["is_json"], False)
38-
self.assertEqual(result["value"], oid)
33+
self.assertEqual(result["value"], "69cd52da72ad0703d3dfef51")
3934

4035
def test_datetime(self):
41-
"""Datetime values as they might appear in a MongoDB doc."""
4236
dt = datetime.datetime(2024, 1, 1, 12, 30)
4337
result = self.form._format_cell_value(dt)
44-
# Datetimes serialize to {"$date": timestamp_ms} which is single-key dict
4538
self.assertIs(result["is_json"], False)
46-
self.assertEqual(result["value"], str(json.loads(json_util.dumps(dt))["$date"]))
39+
self.assertEqual(result["value"], "2024-01-01T12:30:00Z")
4740

4841
def test_embedded_document(self):
4942
embedded_doc = {
@@ -69,6 +62,71 @@ def test_list_field(self):
6962
value_map = {item["key"]: item["value"] for item in result["value"]}
7063
self.assertEqual(value_map[0], "tag1")
7164
self.assertEqual(value_map[1], "tag2")
72-
self.assertEqual(value_map[2], "69cd51ddf1a98c14c906c51e") # ObjectId string
65+
self.assertEqual(value_map[2], "69cd51ddf1a98c14c906c51e")
7366
for item in result["value"]:
7467
self.assertIs(item["is_json"], False)
68+
69+
def test_list_of_embedded_dicts(self):
70+
tags = [
71+
{"name": "cool_tag", "number": 42},
72+
{"name": "other_tag", "number": 7},
73+
]
74+
result = self.form._format_cell_value(tags)
75+
self.assertEqual(result["type"], "list")
76+
self.assertIs(result["is_json"], False)
77+
for item in result["value"]:
78+
self.assertEqual(item["type"], "dict")
79+
self.assertIs(item["is_json"], False)
80+
inner_map = {inner["key"]: inner["value"] for inner in item["value"]}
81+
self.assertIn("name", inner_map)
82+
self.assertIn("number", inner_map)
83+
84+
def test_dict_with_nested_list_of_dicts(self):
85+
address = {
86+
"street": "123 Main St",
87+
"tags": [
88+
{"name": "cool_tag", "number": 42},
89+
],
90+
}
91+
result = self.form._format_cell_value(address)
92+
self.assertEqual(result["type"], "dict")
93+
self.assertIs(result["is_json"], False)
94+
items_by_key = {item["key"]: item for item in result["value"]}
95+
self.assertEqual(items_by_key["street"]["value"], "123 Main St")
96+
tags_item = items_by_key["tags"]
97+
self.assertEqual(tags_item["type"], "list")
98+
self.assertIs(tags_item["is_json"], False)
99+
first_tag = tags_item["value"][0]
100+
self.assertEqual(first_tag["type"], "dict")
101+
inner_map = {inner["key"]: inner["value"] for inner in first_tag["value"]}
102+
self.assertEqual(inner_map["name"], "cool_tag")
103+
self.assertEqual(inner_map["number"], "42")
104+
105+
def test_deeply_nested_embedded_document(self):
106+
address = {
107+
"street": "123 Main St",
108+
"tags": [
109+
{
110+
"name": "cool_tag",
111+
"number": 42,
112+
"person": {"name": "Alice"},
113+
}
114+
],
115+
}
116+
result = self.form._format_cell_value(address)
117+
self.assertEqual(result["type"], "dict")
118+
address_by_key = {item["key"]: item for item in result["value"]}
119+
tags_item = address_by_key["tags"]
120+
self.assertEqual(tags_item["type"], "list")
121+
self.assertIs(tags_item["is_json"], False)
122+
first_tag = tags_item["value"][0]
123+
self.assertEqual(first_tag["type"], "dict")
124+
self.assertIs(first_tag["is_json"], False)
125+
tag_by_key = {inner["key"]: inner for inner in first_tag["value"]}
126+
self.assertEqual(tag_by_key["name"]["value"], "cool_tag")
127+
self.assertEqual(tag_by_key["number"]["value"], "42")
128+
person_item = tag_by_key["person"]
129+
self.assertEqual(person_item["type"], "dict")
130+
self.assertIs(person_item["is_json"], False)
131+
person_map = {inner["key"]: inner["value"] for inner in person_item["value"]}
132+
self.assertEqual(person_map["name"], "Alice")

tests/mql_panel/test_panel.py

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

66
from django_mongodb_extensions.mql_panel import MQLPanel
77

8-
rf = RequestFactory()
8+
request_factory = RequestFactory()
99

1010

1111
def mql_call():
@@ -18,7 +18,7 @@ class MQLPanelTests(TestCase):
1818

1919
def setUp(self):
2020
self._get_response = lambda request: HttpResponse()
21-
self.request = rf.get("/")
21+
self.request = request_factory.get("/")
2222
self.toolbar = DebugToolbar(self.request, self.get_response)
2323
self.toolbar.stats = {}
2424
self.panel = self.toolbar.get_panel_by_id(self.panel_id)

0 commit comments

Comments
 (0)