Skip to content

Commit ade23cf

Browse files
committed
adding Bytea column
1 parent 9a0bc30 commit ade23cf

File tree

4 files changed

+126
-0
lines changed

4 files changed

+126
-0
lines changed

piccolo/columns/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .column_types import ( # noqa: F401
22
Boolean,
3+
Bytea,
34
Date,
45
Decimal,
56
Float,

piccolo/columns/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,8 @@ def get_sql_value(self, value: t.Any) -> t.Any:
437437
output = str(value).lower()
438438
elif isinstance(value, datetime.datetime):
439439
output = f"'{value.isoformat().replace('T', '')}'"
440+
elif isinstance(value, bytes):
441+
output = f"'{value.hex()}'"
440442
else:
441443
output = value
442444

piccolo/columns/column_types.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,3 +1218,64 @@ def get_select_string(self, engine_type: str, just_alias=False) -> str:
12181218
return f"{select_string} {self.json_operator}"
12191219
else:
12201220
return f"{select_string} {self.json_operator} AS {self.alias}"
1221+
1222+
1223+
###############################################################################
1224+
1225+
1226+
class Bytea(Column):
1227+
"""
1228+
Used for storing bytes.
1229+
1230+
**Example**
1231+
1232+
.. code-block:: python
1233+
1234+
class Token(Table):
1235+
token = Bytea(default=b'token123')
1236+
1237+
# Create
1238+
>>> Token(token=b'my-token').save().run_sync()
1239+
1240+
# Query
1241+
>>> Token.select(Token.token).run_sync()
1242+
{'token': b'my-token'}
1243+
1244+
"""
1245+
1246+
@property
1247+
def column_type(self):
1248+
engine_type = self._meta.table._meta.db.engine_type
1249+
if engine_type == "postgres":
1250+
return "BYTEA"
1251+
elif engine_type == "sqlite":
1252+
return "BLOB"
1253+
raise Exception("Unrecognized engine type")
1254+
1255+
def __init__(
1256+
self,
1257+
default: t.Union[
1258+
bytes,
1259+
bytearray,
1260+
t.Callable[[], bytes],
1261+
t.Callable[[], bytearray],
1262+
None,
1263+
] = b"",
1264+
**kwargs,
1265+
) -> None:
1266+
self._validate_default(default, (bytes, bytearray, None))
1267+
1268+
if isinstance(default, bytearray):
1269+
default = bytes(default)
1270+
1271+
self.default = default
1272+
kwargs.update({"default": default})
1273+
super().__init__(**kwargs)
1274+
1275+
1276+
class Blob(Bytea):
1277+
"""
1278+
An alias for Bytea.
1279+
"""
1280+
1281+
pass

tests/columns/test_bytea.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from unittest import TestCase
2+
3+
from piccolo.table import Table
4+
from piccolo.columns.column_types import Bytea
5+
6+
7+
class MyTable(Table):
8+
token = Bytea()
9+
10+
11+
class MyTableDefault(Table):
12+
"""
13+
Test the different default types.
14+
"""
15+
16+
token = Bytea()
17+
token_bytes = Bytea(default=b"my-token")
18+
token_bytearray = Bytea(default=bytearray(b"my-token"))
19+
token_none = Bytea(default=None, null=True)
20+
21+
22+
class TestBytea(TestCase):
23+
def setUp(self):
24+
MyTable.create_table().run_sync()
25+
26+
def tearDown(self):
27+
MyTable.alter().drop_table().run_sync()
28+
29+
def test_bytea(self):
30+
"""
31+
Test storing a valid bytes value.
32+
"""
33+
row = MyTable(token=b"my-token")
34+
row.save().run_sync()
35+
self.assertEqual(row.token, b"my-token")
36+
37+
self.assertEqual(
38+
MyTable.select(MyTable.token).first().run_sync(),
39+
{"token": b"my-token"},
40+
)
41+
42+
43+
class TestByteaDefault(TestCase):
44+
def setUp(self):
45+
MyTableDefault.create_table().run_sync()
46+
47+
def tearDown(self):
48+
MyTableDefault.alter().drop_table().run_sync()
49+
50+
def test_json_default(self):
51+
row = MyTableDefault()
52+
row.save().run_sync()
53+
54+
self.assertEqual(row.token, b"")
55+
self.assertEqual(row.token_bytes, b"my-token")
56+
self.assertEqual(row.token_bytearray, b"my-token")
57+
self.assertEqual(row.token_none, None)
58+
59+
def test_invalid_default(self):
60+
with self.assertRaises(ValueError):
61+
for value in ("a", 1, ("x", "y", "z")):
62+
Bytea(default=value)

0 commit comments

Comments
 (0)