Skip to content

Commit af5158c

Browse files
authored
Merge pull request piccolo-orm#94 from piccolo-orm/array_migrations
Array column serialisation in migrations
2 parents 5d0b740 + af67965 commit af5158c

File tree

3 files changed

+107
-7
lines changed

3 files changed

+107
-7
lines changed

piccolo/apps/migrations/auto/serialisation.py

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
import builtins
23
from copy import deepcopy
34
from dataclasses import dataclass, field
45
import datetime
@@ -8,6 +9,7 @@
89
import typing as t
910
import uuid
1011

12+
from piccolo.columns import Column
1113
from piccolo.columns.defaults.base import Default
1214
from piccolo.columns.reference import LazyTableReference
1315
from piccolo.table import Table
@@ -17,7 +19,21 @@
1719

1820

1921
@dataclass
20-
class SerialisedClass:
22+
class SerialisedBuiltin:
23+
builtin: t.Any
24+
25+
def __hash__(self):
26+
return self.builtin.__name__
27+
28+
def __eq__(self, other):
29+
return self.__hash__() == other.__hash__()
30+
31+
def __repr__(self):
32+
return self.builtin.__name__
33+
34+
35+
@dataclass
36+
class SerialisedClassInstance:
2137
instance: object
2238

2339
def __hash__(self):
@@ -26,19 +42,37 @@ def __hash__(self):
2642
def __eq__(self, other):
2743
return self.__hash__() == other.__hash__()
2844

29-
def _serialise_value(self, value):
30-
return f"'{value}'" if isinstance(value, str) else value
31-
3245
def __repr__(self):
3346
args = ", ".join(
3447
[
35-
f"{key}={self._serialise_value(value)}"
48+
f"{key}={value.__repr__()}"
3649
for key, value in self.instance.__dict__.items()
3750
]
3851
)
3952
return f"{self.instance.__class__.__name__}({args})"
4053

4154

55+
@dataclass
56+
class SerialisedColumnInstance:
57+
instance: Column
58+
serialised_params: SerialisedParams
59+
60+
def __hash__(self):
61+
return self.instance.__hash__()
62+
63+
def __eq__(self, other):
64+
return self.__hash__() == other.__hash__()
65+
66+
def __repr__(self):
67+
args = ", ".join(
68+
[
69+
f"{key}={self.serialised_params.params.get(key).__repr__()}" # noqa: E501
70+
for key in self.instance._meta.params.keys()
71+
]
72+
)
73+
return f"{self.instance.__class__.__name__}({args})"
74+
75+
4276
@dataclass
4377
class SerialisedTableType:
4478
table_type: t.Type[Table]
@@ -127,9 +161,40 @@ def serialise_params(params: t.Dict[str, t.Any]) -> SerialisedParams:
127161

128162
for key, value in params.items():
129163

164+
# Builtins, such as str, list and dict.
165+
if (
166+
hasattr(value, "__module__")
167+
and value.__module__ == builtins.__name__
168+
):
169+
params[key] = SerialisedBuiltin(builtin=value)
170+
continue
171+
172+
# Column instances, which are used by Array definitions.
173+
if isinstance(value, Column):
174+
column: Column = value
175+
serialised_params: SerialisedParams = serialise_params(
176+
params=column._meta.params
177+
)
178+
179+
# Include the extra imports and definitions required for the
180+
# column params.
181+
extra_imports.extend(serialised_params.extra_imports)
182+
extra_definitions.extend(serialised_params.extra_definitions)
183+
184+
extra_imports.append(
185+
Import(
186+
module=column.__class__.__module__,
187+
target=column.__class__.__name__,
188+
)
189+
)
190+
params[key] = SerialisedColumnInstance(
191+
instance=value, serialised_params=serialised_params
192+
)
193+
continue
194+
130195
# Class instances
131196
if isinstance(value, Default):
132-
params[key] = SerialisedClass(instance=value)
197+
params[key] = SerialisedClassInstance(instance=value)
133198
extra_imports.append(
134199
Import(
135200
module=value.__class__.__module__,
@@ -227,7 +292,7 @@ def deserialise_params(params: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
227292
if isinstance(value, str) and not isinstance(value, Enum):
228293
if value != "self":
229294
params[key] = deserialise_legacy_params(name=key, value=value)
230-
elif isinstance(value, SerialisedClass):
295+
elif isinstance(value, SerialisedClassInstance):
231296
params[key] = value.instance
232297
elif isinstance(value, SerialisedUUID):
233298
params[key] = value.instance

piccolo/columns/column_types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,10 @@ def __init__(
13841384

13851385
self._validate_default(default, (list, None))
13861386

1387+
# Usually columns are given a name by the Table metaclass, but in this
1388+
# case we have to assign one manually.
1389+
base_column._meta._name = base_column.__class__.__name__
1390+
13871391
self.base_column = base_column
13881392
self.default = default
13891393
self.index: t.Optional[int] = None

tests/apps/migrations/auto/test_serialisation.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from unittest import TestCase
22

33
from piccolo.apps.migrations.auto.serialisation import serialise_params
4+
from piccolo.columns.column_types import Varchar
45
from piccolo.columns.defaults import DateNow, TimeNow, TimestampNow, UUID4
56
from piccolo.columns.reference import LazyTableReference
67

@@ -90,3 +91,33 @@ def test_lambda(self):
9091
self.assertEqual(
9192
manager.exception.__str__(), "Lambdas can't be serialised"
9293
)
94+
95+
def test_builtins(self):
96+
"""
97+
Make sure builtins can be serialised properly.
98+
"""
99+
serialised = serialise_params(params={"default": list})
100+
self.assertTrue(serialised.params["default"].__repr__() == "list")
101+
102+
self.assertTrue(len(serialised.extra_imports) == 0)
103+
104+
def test_column_instance(self):
105+
"""
106+
Make sure Column instances can be serialised properly. An example
107+
use case is when a `base_column` argument is passed to an `Array`
108+
column.
109+
"""
110+
serialised = serialise_params(params={"base_column": Varchar()})
111+
112+
self.assertEqual(
113+
serialised.params["base_column"].__repr__(),
114+
"Varchar(length=255, default='', null=False, primary=False, key=False, unique=False, index=False, index_method=IndexMethod.btree)", # noqa: E501
115+
)
116+
117+
self.assertEqual(
118+
{i.__repr__() for i in serialised.extra_imports},
119+
{
120+
"from piccolo.columns.column_types import Varchar",
121+
"from piccolo.columns.indexes import IndexMethod",
122+
},
123+
)

0 commit comments

Comments
 (0)