Skip to content

Commit 924a954

Browse files
committed
Address review feedback
1 parent 0234ecb commit 924a954

File tree

6 files changed

+111
-42
lines changed

6 files changed

+111
-42
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: 69 additions & 7 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
@@ -30,20 +29,18 @@ def test_simple_fields(self):
3029
self.assertEqual(self.form._format_cell_value(input_value), expected)
3130

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

4037
def test_datetime(self):
4138
"""Datetime values as they might appear in a MongoDB doc."""
4239
dt = datetime.datetime(2024, 1, 1, 12, 30)
4340
result = self.form._format_cell_value(dt)
4441
# Datetimes serialize to {"$date": timestamp_ms} which is single-key dict
4542
self.assertIs(result["is_json"], False)
46-
self.assertEqual(result["value"], str(json.loads(json_util.dumps(dt))["$date"]))
43+
self.assertEqual(result["value"], "2024-01-01T12:30:00Z")
4744

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

0 commit comments

Comments
 (0)