Skip to content

Commit 8473cbd

Browse files
committed
adding basic foundations for JSON field
1 parent 3d72c4a commit 8473cbd

File tree

5 files changed

+85
-2
lines changed

5 files changed

+85
-2
lines changed

piccolo/columns/base.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def get_select_string(self, engine_type: str, just_alias=False) -> str:
325325
"""
326326
return self._meta.get_full_name(just_alias=just_alias)
327327

328-
def get_sql_value(self, value: t.Any) -> str:
328+
def get_sql_value(self, value: t.Any) -> t.Any:
329329
"""
330330
When using DDL statements, we can't parameterise the values. An example
331331
is when setting the default for a column. So we have to convert from
@@ -390,6 +390,13 @@ def querystring(self) -> QueryString:
390390
if not self._meta.primary:
391391
default = self.get_default_value()
392392
sql_value = self.get_sql_value(value=default)
393+
# Escape the value if it contains a pair of curly braces, otherwise
394+
# an empty value will appear in the compiled querystring.
395+
sql_value = (
396+
sql_value.replace("{}", "{{}}")
397+
if isinstance(sql_value, str)
398+
else sql_value
399+
)
393400
query += f" DEFAULT {sql_value}"
394401

395402
return QueryString(query)

piccolo/columns/column_types.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,3 +1039,34 @@ def __getattribute__(self, name: str):
10391039
return new_column
10401040
else:
10411041
return value
1042+
1043+
1044+
###############################################################################
1045+
1046+
1047+
class JSON(Column):
1048+
"""
1049+
Used for storing JSON strings. In Postgres, the JSON can be queried
1050+
directly. The data is stored as a string. This is preferable to JSONB
1051+
if you just want to store and retrieve JSON without querying it directly.
1052+
1053+
"""
1054+
1055+
value_type = str
1056+
1057+
def __init__(self, default: t.Union[str, None] = "{}", **kwargs) -> None:
1058+
self._validate_default(default, (str, None))
1059+
self.default = default
1060+
kwargs.update({"default": default})
1061+
super().__init__(**kwargs)
1062+
1063+
1064+
class JSONB(Column):
1065+
"""
1066+
Used for storing JSON strings. In Postgres, the JSON can be queried
1067+
directly. The data is stored in a binary format, which makes querying
1068+
faster, but insertion and retrieval slower (as it needs to be converted
1069+
to and from the binary format).
1070+
"""
1071+
1072+
pass

tests/columns/test_json.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from unittest import TestCase
2+
3+
from piccolo.table import Table
4+
from piccolo.columns.column_types import JSON
5+
6+
7+
class MyTable(Table):
8+
json = JSON()
9+
10+
11+
class MyTableDefault(Table):
12+
json = JSON(default="{}")
13+
14+
15+
class TestJSON(TestCase):
16+
def setUp(self):
17+
MyTable.create_table().run_sync()
18+
19+
def tearDown(self):
20+
MyTable.alter().drop_table().run_sync()
21+
22+
def test_json(self):
23+
row = MyTable(json='{"a": 1}')
24+
row.save().run_sync()
25+
self.assertEqual(row.json, '{"a": 1}')
26+
27+
def test_json_default(self):
28+
row = MyTable()
29+
row.save().run_sync()
30+
self.assertEqual(row.json, "{}")

tests/query/test_querystring.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class TestQueryString(TestCase):
1313
def test_compile_string(self):
1414
compiled_string, args = self.qs.compile_string()
1515

16-
self.assertEqual(compiled_string, "SELECT id FROM band WHERE name = $1")
16+
self.assertEqual(
17+
compiled_string, "SELECT id FROM band WHERE name = $1"
18+
)
1719

1820
self.assertEqual(args, ["Pythonistas"])
1921

tests/table/test_insert.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,16 @@ def test_incompatible_type(self):
2929
"""
3030
with self.assertRaises(TypeError):
3131
Band.insert().add(Manager(name="Guido")).run_sync()
32+
33+
def test_insert_curly_braces(self):
34+
"""
35+
You should be able to insert curly braces without an error.
36+
"""
37+
self.insert_rows()
38+
39+
Band.insert(Band(name="{}", popularity=100)).run_sync()
40+
41+
response = Band.select(Band.name).run_sync()
42+
names = [i["name"] for i in response]
43+
44+
self.assertTrue("{}" in names)

0 commit comments

Comments
 (0)