Commits

James Pic committed 6a19eb5

Added date/time/datetime decoding support

Comments (0)

Files changed (6)

jsonfield/__init__.py

 __version__ = open(os.path.join(os.path.dirname(__file__),'VERSION')).read().strip()
 
 from fields import JSONField
+from serializers import JSONEncoder, JSONDecoder

jsonfield/fields.py

             kwargs['default'] = kwargs.get('default', {})
 
         self.db_type_override=kwargs.pop('db_type', None)
+        self.encoder_override=kwargs.pop('encoder', None)
+        self.decoder_override=kwargs.pop('decoder', None)
 
         super(JSONField, self).__init__(*args, **kwargs)
         if 'default' in kwargs:
                 if self.blank:
                     return ""
             try:
-                value = json.loads(value)
+                value = json.loads(value, cls=self.decoder_override)
             except ValueError:
                 msg = self.error_messages['invalid'] % str(value)
                 raise ValidationError(msg)
             if not self.null and self.blank:
                 return ""
             return None
-        return json.dumps(value, default=default, cls=DjangoJSONEncoder)
+        return json.dumps(value, default=default, cls=self.encoder_override)
 
     def get_prep_lookup(self, lookup_type, value):
         if lookup_type in ["exact", "iexact"]:

jsonfield/serializers.py

+import datetime
+
+from django.utils.timezone import utc
+from django.utils import simplejson
+
+
+class JSONDecoder(simplejson.JSONDecoder):
+    def decode(self, s):
+        data = super(JSONDecoder, self).decode(s)
+
+        for k, v in data.items():
+            if isinstance(v, dict):
+                if v.keys() == ['__pythonclass__']:
+                    data[k] = self.handle_pythonclass(v)
+
+        return data
+
+    def handle_pythonclass(self, data):
+        if data[u'__pythonclass__'][0] == u'datetime.datetime':
+            value = datetime.datetime(*data[u'__pythonclass__'][1]).replace(
+                    tzinfo=utc)
+        elif data[u'__pythonclass__'][0] == u'datetime.time':
+            value = datetime.time(*data[u'__pythonclass__'][1]).replace(
+                    tzinfo=utc)
+        elif data[u'__pythonclass__'][0] == u'datetime.date':
+            value = datetime.date(*data[u'__pythonclass__'][1])
+        else:
+            raise Exception(u'Cannot decode %s' % data)
+
+        return value
+
+
+class JSONEncoder(simplejson.JSONEncoder):
+    """
+    JSONEncoder subclass that knows how to encode date/time and decimal types.
+    """
+    def default(self, o):
+        # See "Date Time String Format" in the ECMA-262 specification.
+        if isinstance(o, datetime.datetime):
+            r = o.isoformat()
+            if o.microsecond:
+                r = r[:23] + r[26:]
+            if r.endswith('+00:00'):
+                r = r[:-6] + 'Z'
+            return r
+        elif isinstance(o, datetime.date):
+            return o.isoformat()
+        elif isinstance(o, datetime.time):
+            if is_aware(o):
+                raise ValueError("JSON can't represent timezone-aware times.")
+            r = o.isoformat()
+            if o.microsecond:
+                r = r[:12]
+            return r
+        elif isinstance(o, decimal.Decimal):
+            return str(o)
+        else:
+            return super(DjangoJSONEncoder, self).default(o)
+
+

jsonfield/tests/__init__.py

 from base import *
+from dt import *

jsonfield/tests/dt.py

+#:coding=utf-8:
+import datetime
+
+from django.utils.timezone import utc
+from django.test import TestCase as DjangoTestCase
+from django.utils import unittest
+
+from jsonfield.tests.jsonfield_test_app.models import *
+from jsonfield import JSONField
+
+
+class JSONFieldDateTimeTest(DjangoTestCase):
+    def test_datetime_decode(self):
+        obj = DateTimeJSONFieldTestModel(json='''{
+            "spam": {
+                "__pythonclass__": [
+                    "datetime.datetime",
+                    [2012, 6, 7, 9, 16, 28, 249000]
+                ]
+            }
+        }''')
+        self.assertEquals(obj.json, {'spam':
+            datetime.datetime(2012, 6, 7, 9, 16, 28, 249000, utc)})
+
+    def test_time_decode(self):
+        obj = DateTimeJSONFieldTestModel(json='''{
+            "spam": {
+                "__pythonclass__": [
+                    "datetime.time",
+                    [9, 16, 28, 249000]
+                ]
+            }
+        }''')
+        self.assertEquals(obj.json, {'spam':
+            datetime.time(9, 16, 28, 249000, tzinfo=utc)})
+
+    def test_date_decode(self):
+        obj = DateTimeJSONFieldTestModel(json='''{
+            "spam": {
+                "__pythonclass__": [
+                    "datetime.date",
+                    [2012, 6, 7]
+                ]
+            }
+        }''')
+        self.assertEquals(obj.json, {'spam':
+            datetime.date(2012, 6, 7)})

jsonfield/tests/jsonfield_test_app/models.py

 from django.db import models
-from jsonfield import JSONField
+from jsonfield import JSONField, JSONEncoder, JSONDecoder
 
 class JSONFieldTestModel(models.Model):
     json = JSONField(u"test", null=True, blank=True)
     null_json = JSONField(null=True)
     blank_json = JSONField(blank=True)
     class Meta:
-        app_label = 'jsonfield'
+        app_label = 'jsonfield'
+
+class DateTimeJSONFieldTestModel(models.Model):
+    json = JSONField(u"test", null=True, blank=True,
+            encoder=JSONEncoder, decoder=JSONDecoder)
+    class Meta:
+        app_label = 'jsonfield'