Skip to content

Commit c51e654

Browse files
author
Tim Heap
committed
Support Django serialization framework, and dumpdata/loaddata
This required adding BaseField.value_to_string() and BaseField.to_python() methods.
1 parent 08a81a8 commit c51e654

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

postgres_composite_types/__init__.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"""
3636

3737
import inspect
38+
import json
3839
import logging
3940
import sys
4041
from collections import OrderedDict
@@ -132,6 +133,36 @@ def formfield(self, **kwargs): # pylint:disable=arguments-differ
132133

133134
return super().formfield(**defaults)
134135

136+
def to_python(self, value):
137+
"""
138+
Convert a value to the correct type for this field. Values from the
139+
database will already be of the correct type, due to the the caster
140+
registered with psycopg2. The field can also be serialized as a string
141+
via value_to_string, where it is encoded as a JSON object.
142+
"""
143+
# Composite types are serialized as JSON blobs. If BaseField.to_python
144+
# is called with a string, assume it was produced by value_to_string
145+
# and decode it
146+
if isinstance(value, str):
147+
value = json.loads(value)
148+
return self.Meta.model(**{
149+
name: field.to_python(value.get(name))
150+
for name, field in self.Meta.fields
151+
})
152+
153+
return super().to_python(value)
154+
155+
def value_to_string(self, obj):
156+
"""
157+
Serialize this as a JSON object {name: field.value_to_string(...)} for
158+
each child field.
159+
"""
160+
value = self.value_from_object(obj)
161+
return json.dumps({
162+
name: field.value_to_string(value)
163+
for name, field in self.Meta.fields
164+
})
165+
135166

136167
class BaseOperation(migrations.operations.base.Operation):
137168
"""Base class for the DB operation that relates to this type."""

tests/test_field.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import datetime
44
from unittest import mock
55

6+
from django.core import serializers
67
from django.db import connection
78
from django.db.migrations.executor import MigrationExecutor
89
from django.test import TestCase, TransactionTestCase
@@ -148,6 +149,21 @@ def test_adapted_sql(self):
148149
b"(1, 'b', '1985-10-26T09:00:00'::timestamp)::test_type",
149150
adapted.getquoted())
150151

152+
def test_serialize(self):
153+
"""
154+
Check that composite values are correctly handled through Django's
155+
serialize/deserialize helpers, used for dumpdata/loaddata.
156+
"""
157+
old = Item(
158+
name="table",
159+
bounding_box=Box(top_left=Point(x=1, y=1),
160+
bottom_right=Point(x=4, y=2)))
161+
out = serializers.serialize("json", [old])
162+
new = next(serializers.deserialize("json", out)).object
163+
164+
self.assertEqual(old.bounding_box,
165+
new.bounding_box)
166+
151167

152168
class TestOptionalFields(TestCase):
153169
"""

0 commit comments

Comments
 (0)