Commit 2c96b3da authored by Matthew Somerville's avatar Matthew Somerville Committed by Tim Graham
Browse files

[1.8.x] Refs #24937 -- Backported more commits to fix for serialization of Date(Time)RangeField.

Instead of using DjangoJSONEncoder, use base_field's value_to_string().

Note this means the serialization of e.g. IntegerRangeField now has
strings for lower and upper, so use to_python when they came back in
(same behaviour as ArrayField, hopefully, from where I also got the
set_attributes_from_name function).

Backport of 86d9b10d and
8a842148 from master
parent 3ded51bc
Loading
Loading
Loading
Loading
+2 −5
Original line number Diff line number Diff line
@@ -8,12 +8,9 @@ from django.db.models import Field, IntegerField, Transform
from django.utils import six
from django.utils.translation import string_concat, ugettext_lazy as _

__all__ = ['ArrayField']

from .utils import AttributeSetter

class AttributeSetter(object):
    def __init__(self, name, value):
        setattr(self, name, value)
__all__ = ['ArrayField']


class ArrayField(Field):
+18 −7
Original line number Diff line number Diff line
@@ -3,10 +3,11 @@ import json
from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange, Range

from django.contrib.postgres import forms, lookups
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils import six

from .utils import AttributeSetter

__all__ = [
    'RangeField', 'IntegerRangeField', 'BigIntegerRangeField',
    'FloatRangeField', 'DateTimeRangeField', 'DateRangeField',
@@ -27,22 +28,32 @@ class RangeField(models.Field):

    def to_python(self, value):
        if isinstance(value, six.string_types):
            value = self.range_type(**json.loads(value))
            # Assume we're deserializing
            vals = json.loads(value)
            for end in ('lower', 'upper'):
                if end in vals:
                    vals[end] = self.base_field.to_python(vals[end])
            value = self.range_type(**vals)
        elif isinstance(value, (list, tuple)):
            value = self.range_type(value[0], value[1])
        return value

    def set_attributes_from_name(self, name):
        super(RangeField, self).set_attributes_from_name(name)
        self.base_field.set_attributes_from_name(name)

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        if value is None:
            return None
        if value.isempty:
            return json.dumps({"empty": True})
        return json.dumps({
            "lower": value.lower,
            "upper": value.upper,
            "bounds": value._bounds,
        }, cls=DjangoJSONEncoder)
        base_field = self.base_field
        result = {"bounds": value._bounds}
        for end in ('lower', 'upper'):
            obj = AttributeSetter(base_field.attname, getattr(value, end))
            result[end] = base_field.value_to_string(obj)
        return json.dumps(result)

    def formfield(self, **kwargs):
        kwargs.setdefault('form_class', self.form_field)
+3 −0
Original line number Diff line number Diff line
class AttributeSetter(object):
    def __init__(self, name, value):
        setattr(self, name, value)
+5 −5
Original line number Diff line number Diff line
@@ -193,18 +193,18 @@ class TestQuerying(TestCase):
@skipUnlessPG92
class TestSerialization(TestCase):
    test_data = (
        '[{"fields": {"ints": "{\\"upper\\": 10, \\"lower\\": 0, '
        '[{"fields": {"ints": "{\\"upper\\": \\"10\\", \\"lower\\": \\"0\\", '
        '\\"bounds\\": \\"[)\\"}", "floats": "{\\"empty\\": true}", '
        '"bigints": null, "timestamps": "{\\"upper\\": \\"2014-02-02T12:12:12\\", '
        '\\"lower\\": \\"2014-01-01T00:00:00\\", \\"bounds\\": \\"[)\\"}", '
        '"bigints": null, "timestamps": "{\\"upper\\": \\"2014-02-02T12:12:12+00:00\\", '
        '\\"lower\\": \\"2014-01-01T00:00:00+00:00\\", \\"bounds\\": \\"[)\\"}", '
        '"dates": "{\\"upper\\": \\"2014-02-02\\", \\"lower\\": \\"2014-01-01\\", \\"bounds\\": \\"[)\\"}" }, '
        '"model": "postgres_tests.rangesmodel", "pk": null}]'
    )

    lower_date = datetime.date(2014, 1, 1)
    upper_date = datetime.date(2014, 2, 2)
    lower_dt = datetime.datetime(2014, 1, 1, 0, 0, 0)
    upper_dt = datetime.datetime(2014, 2, 2, 12, 12, 12)
    lower_dt = datetime.datetime(2014, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
    upper_dt = datetime.datetime(2014, 2, 2, 12, 12, 12, tzinfo=timezone.utc)

    def test_dumping(self):
        instance = RangesModel(ints=NumericRange(0, 10), floats=NumericRange(empty=True),