Skip to content

Commit 5012db7

Browse files
authored
1383 load_json doesn't work on nested objects (#1384)
* load json on nested objects * add a test
1 parent d15d1f1 commit 5012db7

3 files changed

Lines changed: 67 additions & 12 deletions

File tree

piccolo/query/base.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,14 @@ async def _process_results(self, results) -> QueryResponseType:
110110
if output._output.nested:
111111
return cast(
112112
QueryResponseType,
113-
[make_nested_object(row, self.table) for row in raw],
113+
[
114+
make_nested_object(
115+
row,
116+
self.table,
117+
load_json=output._output.load_json,
118+
)
119+
for row in raw
120+
],
114121
)
115122
else:
116123
return cast(

piccolo/utils/objects.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
from typing import TYPE_CHECKING, Any
44

55
from piccolo.columns.column_types import ForeignKey
6+
from piccolo.utils import encoding
67

78
if TYPE_CHECKING: # pragma: no cover
89
from piccolo.table import Table
910

1011

11-
def make_nested_object(row: dict[str, Any], table_class: type[Table]) -> Table:
12+
def make_nested_object(
13+
row: dict[str, Any],
14+
table_class: type[Table],
15+
load_json: bool = False,
16+
) -> Table:
1217
"""
1318
Takes a nested dictionary such as this:
1419
@@ -38,6 +43,12 @@ def make_nested_object(row: dict[str, Any], table_class: type[Table]) -> Table:
3843
"""
3944
table_params: dict[str, Any] = {}
4045

46+
json_column_names = (
47+
[column._meta.name for column in table_class._meta.json_columns]
48+
if load_json
49+
else []
50+
)
51+
4152
for key, value in row.items():
4253
if isinstance(value, dict):
4354
# This is probably a related table.
@@ -51,12 +62,15 @@ def make_nested_object(row: dict[str, Any], table_class: type[Table]) -> Table:
5162
fk_column._foreign_key_meta.resolved_references
5263
)
5364
table_params[key] = make_nested_object(
54-
value, related_table_class
65+
value,
66+
related_table_class,
67+
load_json=load_json,
5568
)
5669
else:
5770
# The value doesn't belong to a foreign key, so just append it.
5871
table_params[key] = value
59-
72+
elif load_json and key in json_column_names:
73+
table_params[key] = encoding.load_json(value)
6074
else:
6175
table_params[key] = value
6276

tests/table/test_join.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import decimal
2-
from unittest import TestCase
32

3+
from piccolo.testing.test_case import TableTest
44
from tests.base import engine_is
55
from tests.example_apps.music.tables import (
66
Band,
77
Concert,
8+
Instrument,
89
Manager,
10+
RecordingStudio,
911
Signing,
1012
Ticket,
1113
Venue,
@@ -23,12 +25,20 @@ def test_create_join(self):
2325
table.alter().drop_table().run_sync()
2426

2527

26-
class TestJoin(TestCase):
27-
tables = [Manager, Band, Venue, Concert, Ticket, Signing]
28+
class TestJoin(TableTest):
29+
tables = [
30+
Manager,
31+
Band,
32+
Venue,
33+
Concert,
34+
Ticket,
35+
Signing,
36+
Instrument,
37+
RecordingStudio,
38+
]
2839

2940
def setUp(self):
30-
for table in self.tables:
31-
table.create_table().run_sync()
41+
super().setUp()
3242

3343
manager_1 = Manager(name="Guido")
3444
manager_1.save().run_sync()
@@ -56,9 +66,13 @@ def setUp(self):
5666
signing = Signing(with_=band_1)
5767
signing.save().run_sync()
5868

59-
def tearDown(self):
60-
for table in reversed(self.tables):
61-
table.alter().drop_table().run_sync()
69+
recording_studio = RecordingStudio(facilities={"restaurant": True})
70+
recording_studio.save().run_sync()
71+
72+
instrument = Instrument(
73+
name="Piccolo", recording_studio=recording_studio
74+
)
75+
instrument.save().run_sync()
6276

6377
###########################################################################
6478

@@ -399,6 +413,26 @@ def test_objects__all_related__deep(self):
399413
self.assertIsInstance(ticket.concert.band_1.manager, Manager)
400414
self.assertIsInstance(ticket.concert.band_2.manager, Manager)
401415

416+
def test_objects_nested_with_load_json(self):
417+
"""
418+
Make sure that nested objects works alongside ``load_json`` (i.e. the
419+
JSON on nested objects gets loaded).
420+
421+
https://github.com/piccolo-orm/piccolo/issues/1383
422+
423+
"""
424+
instrument = (
425+
Instrument.objects(Instrument.recording_studio)
426+
.output(load_json=True)
427+
.first()
428+
.run_sync()
429+
)
430+
assert instrument is not None
431+
self.assertDictEqual(
432+
instrument.recording_studio.facilities,
433+
{"restaurant": True},
434+
)
435+
402436
def test_objects_prefetch_clause(self):
403437
"""
404438
Make sure that ``prefetch`` clause works correctly.

0 commit comments

Comments
 (0)