Skip to content

Commit 20c7bfc

Browse files
committed
Address review feedback
1 parent a7b537a commit 20c7bfc

File tree

3 files changed

+104
-17
lines changed

3 files changed

+104
-17
lines changed

django_mongodb_extensions/mql_panel/forms.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,58 @@ def _execute_query(self, db, collection, collection_name, operation, args_list):
172172
raise ValueError(f"Unsupported read operation: {operation}")
173173
return self.convert_documents_to_table(result_docs)
174174

175+
def _flatten_single_key_dicts(self, obj):
176+
if isinstance(obj, dict):
177+
if len(obj) == 1:
178+
only_value = next(iter(obj.values()))
179+
return self._flatten_single_key_dicts(only_value)
180+
return {
181+
key_name: self._flatten_single_key_dicts(value_item)
182+
for key_name, value_item in obj.items()
183+
}
184+
elif isinstance(obj, list):
185+
return [self._flatten_single_key_dicts(value_item) for value_item in obj]
186+
return obj
187+
175188
def _format_cell_value(self, value):
176-
"""Format a single cell value for HTML table display."""
177-
if isinstance(value, (str, int, float, bool)):
178-
return {"value": str(value), "is_json": False}
179189
serialized = json_util.dumps(value)
180-
parsed = json.loads(serialized)
181-
if isinstance(parsed, dict) and len(parsed) == 1:
182-
key, val = next(iter(parsed.items()))
183-
return {"value": str(val), "is_json": False}
184-
if isinstance(parsed, dict) and len(parsed) > 1:
185-
return {"value": json.dumps(parsed, indent=4), "is_json": True}
186-
return {"value": serialized, "is_json": False}
190+
parsed_json = json.loads(serialized)
191+
flattened_value = self._flatten_single_key_dicts(parsed_json)
192+
if isinstance(flattened_value, (str, int, float, bool)):
193+
return {"value": str(flattened_value), "is_json": False}
194+
if isinstance(flattened_value, dict):
195+
return {
196+
"type": "dict",
197+
"value": self._format_dict_for_template(flattened_value),
198+
"is_json": False,
199+
}
200+
if isinstance(flattened_value, list):
201+
return {
202+
"type": "list",
203+
"value": self._format_list_for_template(flattened_value),
204+
"is_json": False,
205+
}
206+
return {
207+
"value": json.dumps(flattened_value, indent=4),
208+
"is_json": True,
209+
}
210+
211+
def _format_dict_for_template(self, dictionary):
212+
return [
213+
{"key": key_name, **self._format_cell_value(value_item)}
214+
for key_name, value_item in dictionary.items()
215+
]
216+
217+
def _format_list_for_template(self, list_items):
218+
return [
219+
{"key": index, **self._format_cell_value(value_item)}
220+
for index, value_item in enumerate(list_items)
221+
]
222+
223+
def _format_row(self, row_dict):
224+
return [self._format_cell_value(cell_value) for cell_value in row_dict.values()]
187225

188226
def convert_documents_to_table(self, documents):
189-
"""Convert MongoDB documents to a table of rows and headers."""
190227
if not documents:
191228
return [], []
192229
# Collect all unique field names

django_mongodb_extensions/templates/mql_panel/mql_query.html

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ <h3>{% translate "Query Results" %}</h3>
1313
<dt>{% translate "Database" %}</dt>
1414
<dd>{{ alias }}</dd>
1515
</dl>
16+
1617
<h4>{% translate "Query Results" %}</h4>
1718
<table>
1819
<thead>
@@ -27,7 +28,54 @@ <h4>{% translate "Query Results" %}</h4>
2728
<tr>
2829
{% for column in row %}
2930
<td>
30-
{% if column.is_json %}
31+
{% if column.type == "dict" %}
32+
<table class="subtable">
33+
{% for sub in column.value %}
34+
<tr>
35+
<th>{{ sub.key }}</th>
36+
<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 %}
52+
</td>
53+
</tr>
54+
{% endfor %}
55+
</table>
56+
{% elif column.type == "list" %}
57+
<ul>
58+
{% for sub in column.value %}
59+
<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 %}
75+
</li>
76+
{% endfor %}
77+
</ul>
78+
{% elif column.is_json %}
3179
<pre style="margin: 0; padding: 0; border: none; background: none; white-space: pre-wrap; word-wrap: break-word; font-family: inherit;">{{ column.value }}</pre>
3280
{% else %}
3381
{{ column.value }}

tests/mql_panel/test_forms.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ def test_objectid(self):
5959
oid = ObjectId()
6060
result = self.form._format_cell_value(oid)
6161
# ObjectIds serialize to {"$oid": "<id>"} which is single-key
62-
self.assertFalse(result["is_json"])
62+
self.assertIs(result["is_json"], False)
6363
self.assertEqual(result["value"], str(json.loads(json_util.dumps(oid))["$oid"]))
6464

6565
def test_datetime(self):
6666
"""Datetime values as they might appear in a MongoDB doc."""
6767
dt = datetime.datetime(2024, 1, 1, 12, 30)
6868
result = self.form._format_cell_value(dt)
6969
# Datetimes serialize to {"$date": timestamp_ms} which is single-key dict
70-
self.assertFalse(result["is_json"])
70+
self.assertIs(result["is_json"], False)
7171
self.assertEqual(result["value"], str(json.loads(json_util.dumps(dt))["$date"]))
7272

7373
def test_embedded_document(self):
@@ -78,7 +78,7 @@ def test_embedded_document(self):
7878
"created": datetime.datetime(2024, 2, 1, 18, 0),
7979
}
8080
result = self.form._format_cell_value(embedded_doc)
81-
self.assertTrue(result["is_json"])
81+
self.assertIs(result["is_json"], False)
8282
parsed_back = json.loads(result["value"])
8383
# Datetime will still be JSON date dict
8484
self.assertEqual(parsed_back["name"], "Alice")
@@ -90,5 +90,7 @@ def test_list_field(self):
9090
arr = ["tag1", "tag2", ObjectId()]
9191
result = self.form._format_cell_value(arr)
9292
# Lists are not dicts, so is_json = False
93-
self.assertFalse(result["is_json"])
94-
self.assertEqual(result["value"], json_util.dumps(arr))
93+
self.assertIs(result["is_json"], False)
94+
self.assertEqual(
95+
result["value"], '["tag1", "tag2", {"$oid": "69cc8219f859271f0a081538"}]'
96+
)

0 commit comments

Comments
 (0)