Merge pull request #4818 from dracos/24937-ranging-to-victory
Fixed #24937 -- fix serialization of Date(Time)RangeField.
This commit is contained in:
commit
d58816bd6b
|
@ -8,14 +8,11 @@ from django.db.models import Field, IntegerField, Transform
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.translation import string_concat, ugettext_lazy as _
|
from django.utils.translation import string_concat, ugettext_lazy as _
|
||||||
|
|
||||||
|
from .utils import AttributeSetter
|
||||||
|
|
||||||
__all__ = ['ArrayField']
|
__all__ = ['ArrayField']
|
||||||
|
|
||||||
|
|
||||||
class AttributeSetter(object):
|
|
||||||
def __init__(self, name, value):
|
|
||||||
setattr(self, name, value)
|
|
||||||
|
|
||||||
|
|
||||||
class ArrayField(Field):
|
class ArrayField(Field):
|
||||||
empty_strings_allowed = False
|
empty_strings_allowed = False
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.contrib.postgres import forms, lookups
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
from .utils import AttributeSetter
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'RangeField', 'IntegerRangeField', 'BigIntegerRangeField',
|
'RangeField', 'IntegerRangeField', 'BigIntegerRangeField',
|
||||||
'FloatRangeField', 'DateTimeRangeField', 'DateRangeField',
|
'FloatRangeField', 'DateTimeRangeField', 'DateRangeField',
|
||||||
|
@ -26,22 +28,32 @@ class RangeField(models.Field):
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if isinstance(value, six.string_types):
|
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)):
|
elif isinstance(value, (list, tuple)):
|
||||||
value = self.range_type(value[0], value[1])
|
value = self.range_type(value[0], value[1])
|
||||||
return value
|
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):
|
def value_to_string(self, obj):
|
||||||
value = self._get_val_from_obj(obj)
|
value = self._get_val_from_obj(obj)
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
if value.isempty:
|
if value.isempty:
|
||||||
return json.dumps({"empty": True})
|
return json.dumps({"empty": True})
|
||||||
return json.dumps({
|
base_field = self.base_field
|
||||||
"lower": value.lower,
|
result = {"bounds": value._bounds}
|
||||||
"upper": value.upper,
|
for end in ('lower', 'upper'):
|
||||||
"bounds": value._bounds,
|
obj = AttributeSetter(base_field.attname, getattr(value, end))
|
||||||
})
|
result[end] = base_field.value_to_string(obj)
|
||||||
|
return json.dumps(result)
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
kwargs.setdefault('form_class', self.form_field)
|
kwargs.setdefault('form_class', self.form_field)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
class AttributeSetter(object):
|
||||||
|
def __init__(self, name, value):
|
||||||
|
setattr(self, name, value)
|
|
@ -102,6 +102,10 @@ Minor features
|
||||||
* Added :class:`~django.contrib.postgres.fields.JSONField`.
|
* Added :class:`~django.contrib.postgres.fields.JSONField`.
|
||||||
* Added :doc:`/ref/contrib/postgres/aggregates`.
|
* Added :doc:`/ref/contrib/postgres/aggregates`.
|
||||||
|
|
||||||
|
* Fixed serialization of
|
||||||
|
:class:`~django.contrib.postgres.fields.DateRangeField` and
|
||||||
|
:class:`~django.contrib.postgres.fields.DateTimeRangeField`.
|
||||||
|
|
||||||
:mod:`django.contrib.redirects`
|
:mod:`django.contrib.redirects`
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -291,26 +291,37 @@ class TestQueringWithRanges(TestCase):
|
||||||
@skipUnlessPG92
|
@skipUnlessPG92
|
||||||
class TestSerialization(TestCase):
|
class TestSerialization(TestCase):
|
||||||
test_data = (
|
test_data = (
|
||||||
'[{"fields": {"ints": "{\\"upper\\": 10, \\"lower\\": 0, '
|
'[{"fields": {"ints": "{\\"upper\\": \\"10\\", \\"lower\\": \\"0\\", '
|
||||||
'\\"bounds\\": \\"[)\\"}", "floats": "{\\"empty\\": true}", '
|
'\\"bounds\\": \\"[)\\"}", "floats": "{\\"empty\\": true}", '
|
||||||
'"bigints": null, "timestamps": null, "dates": null}, '
|
'"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}]'
|
'"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, tzinfo=timezone.utc)
|
||||||
|
upper_dt = datetime.datetime(2014, 2, 2, 12, 12, 12, tzinfo=timezone.utc)
|
||||||
|
|
||||||
def test_dumping(self):
|
def test_dumping(self):
|
||||||
instance = RangesModel(ints=NumericRange(0, 10), floats=NumericRange(empty=True))
|
instance = RangesModel(ints=NumericRange(0, 10), floats=NumericRange(empty=True),
|
||||||
|
timestamps=DateTimeTZRange(self.lower_dt, self.upper_dt),
|
||||||
|
dates=DateRange(self.lower_date, self.upper_date))
|
||||||
data = serializers.serialize('json', [instance])
|
data = serializers.serialize('json', [instance])
|
||||||
dumped = json.loads(data)
|
dumped = json.loads(data)
|
||||||
dumped[0]['fields']['ints'] = json.loads(dumped[0]['fields']['ints'])
|
for field in ('ints', 'dates', 'timestamps'):
|
||||||
|
dumped[0]['fields'][field] = json.loads(dumped[0]['fields'][field])
|
||||||
check = json.loads(self.test_data)
|
check = json.loads(self.test_data)
|
||||||
check[0]['fields']['ints'] = json.loads(check[0]['fields']['ints'])
|
for field in ('ints', 'dates', 'timestamps'):
|
||||||
|
check[0]['fields'][field] = json.loads(check[0]['fields'][field])
|
||||||
self.assertEqual(dumped, check)
|
self.assertEqual(dumped, check)
|
||||||
|
|
||||||
def test_loading(self):
|
def test_loading(self):
|
||||||
instance = list(serializers.deserialize('json', self.test_data))[0].object
|
instance = list(serializers.deserialize('json', self.test_data))[0].object
|
||||||
self.assertEqual(instance.ints, NumericRange(0, 10))
|
self.assertEqual(instance.ints, NumericRange(0, 10))
|
||||||
self.assertEqual(instance.floats, NumericRange(empty=True))
|
self.assertEqual(instance.floats, NumericRange(empty=True))
|
||||||
self.assertEqual(instance.dates, None)
|
self.assertEqual(instance.bigints, None)
|
||||||
|
|
||||||
|
|
||||||
class TestValidators(PostgreSQLTestCase):
|
class TestValidators(PostgreSQLTestCase):
|
||||||
|
|
Loading…
Reference in New Issue