Commits

Matthew Schinckel committed a6f6d0a Merge

Merge

Comments (0)

Files changed (13)

 7e5feb505636ecda792a831801a55f933e49ef5f 0.9.3
 44fc45bf5af73abbf583759a450ff7cbe0b65371 0.9.4
 c26c5c6775acb405823490839e136f68d2cc3f87 0.9.6
+d36395a90207dd364e227e42bdc1eff63a634638 0.9.7
+2bee5847a833992cc73bdcf140197581d5a96032 0.9.8
+929dd3e646a191601fc6d2d6160b4be5e0c406b3 0.9.9
+121d790aab8b94ab1961e2ff55ce6ff94ad45851 0.9.10
+11023bad765775de9386bb3b3ee8c3117e12337d 0.9.11
+9841e553355c3aa55e1a1bec929260a63058aa41 0.9.12
 recursive-include jsonfield *.py
-recursive-include jsonfield VERSION
+include jsonfield/VERSION
 include README.rst
 include tests.py
 include LICENSE
+recursive-exclude jsonfield *.pyc
 History
 ----------
 
-0.9.7
+0.9.12
+~~~~~~
+Cache the result of db_type.
+Handle incoming data from multiple select widget better.
+
+0.9.9
+~~~~~
+Finally strip out non-required files.
+
+0.9.8
 ~~~~~
 Remove freezegun workarounds.
+Fix broken build.
 
 0.9.4
 ~~~~~

jsonfield/VERSION

-0.9.6
+0.9.12

jsonfield/fields.py

 from __future__ import unicode_literals
+import json
 
 from django.core.exceptions import ValidationError
 from django.conf import settings
 from django.db import models, DatabaseError, transaction
-from django.utils import simplejson as json
 from django.utils.translation import ugettext_lazy as _
 from django.utils import six
+from django.core.cache import cache
 
 from decimal import Decimal
 import datetime
 from .utils import default
 from .widgets import JSONWidget
 from .forms import JSONFormField
+from jsonfield import __version__
+
+DB_TYPE_CACHE_KEY = (
+    'django-jsonfield:db-type:%s' % __version__ +
+    '%(ENGINE)s:%(HOST)s:%(PORT)s:%(NAME)s'
+)
 
 class JSONField(six.with_metaclass(models.SubfieldBase, models.Field)):
     """
         return 'TextField'
     
     def db_type(self, connection):
-        # Test to see if we support JSON querying.
-        # (Protip: nothing does, at this stage).
-        cursor = connection.cursor()
-        try:
-            sid = transaction.savepoint()
-            cursor.execute('SELECT \'{}\'::json = \'{}\'::json;')
-        except DatabaseError:
-            transaction.savepoint_rollback(sid)
-            return 'text'
-        else:
-            return 'json'
+        cache_key = DB_TYPE_CACHE_KEY % connection.settings_dict
+        db_type = cache.get(cache_key)
+        
+        if not db_type:
+            # Test to see if we support JSON querying.
+            cursor = connection.cursor()
+            try:
+                sid = transaction.savepoint(using=connection.alias)
+                cursor.execute('SELECT \'{}\'::json = \'{}\'::json;')
+            except DatabaseError:
+                transaction.savepoint_rollback(sid, using=connection.alias)
+                db_type = 'text'
+            else:
+                db_type = 'json'
+            cache.set(cache_key, db_type)
+        
+        return db_type
     
     def to_python(self, value):
         if isinstance(value, six.string_types):

jsonfield/forms.py

+import json
+
 from django import forms
-from django.utils import simplejson as json
 from django.utils import six
 
 from .widgets import JSONWidget
 
 
 class JSONFormField(forms.CharField):
+    empty_values = [None, '']
+    
     def __init__(self, *args, **kwargs):
         if 'widget' not in kwargs:
             kwargs['widget'] = JSONWidget
         super(JSONFormField, self).__init__(*args, **kwargs)
 
-    def clean(self, value):
-        """
-        The default is to have a TextField, and we will decode the string
-        that comes back from this. However, another use of this field is
-        to store a list of values, and use these in a MultipleSelect
-        widget. So, if we have an object that isn't a string, then for now
-        we will assume that is where it has come from.
-        """
-        value = super(JSONFormField, self).clean(value)
-
-        if not value:
-            return value
-
-        if isinstance(value, six.string_types):
+    def to_python(self, value):
+        if isinstance(value, six.string_types) and value:
             try:
                 return json.loads(value)
-            except Exception as exc:
+            except ValueError as exc:
                 raise forms.ValidationError(
-                    'JSON decode error: %s' % (unicode(exc),)
+                    'JSON decode error: %s' % (six.u(exc.args[0]),)
                 )
         else:
             return value
+    
+    def validate(self, value):
+        # This is required in older django versions.
+        if value in self.empty_values and self.required:
+            raise forms.ValidationError(self.error_messages['required'], code='required')

jsonfield/templatetags/jsonify.py

+import json
+
 from django import template
-from django.utils import simplejson as json
 from django.utils.safestring import mark_safe
 from jsonfield.utils import TZAwareJSONEncoder
 
 
 @register.filter
 def jsonify(value):
+    # If we have a queryset, then convert it into a list.
     if getattr(value, 'all', False):
         value = list(value)
     return mark_safe(json.dumps(value, cls=TZAwareJSONEncoder))

jsonfield/tests/test_fields.py

     def test_formfield_null_and_blank_clean_none(self):
         field = JSONField("test", null=True, blank=True)
         formfield = field.formfield()
-        self.assertEquals(formfield.clean(value=None), '')
+        self.assertEquals(formfield.clean(value=None), None)
 
     def test_formfield_blank_clean_blank(self):
         field = JSONField("test", null=False, blank=True)
         self.assertEquals(formfield.clean(value=''), '')
 
     def test_formfield_blank_clean_none(self):
+        # Hmm, I'm not sure how to do this. What happens if we pass a
+        # None to a field that has null=False?
         field = JSONField("test", null=False, blank=True)
         formfield = field.formfield()
-        self.assertEquals(formfield.clean(value=None), '')
+        self.assertEquals(formfield.clean(value=None), None)
 
     def test_default_value(self):
         obj = JSONFieldWithDefaultTestModel.objects.create()
 
     def test_invalid_json_default(self):
         with self.assertRaises(ValueError):
-            field = JSONField('test', default='{"foo"}')
+            field = JSONField('test', default='{"foo"}')
+
+class SavingModelsTest(DjangoTestCase):
+    def test_saving_null(self):
+        obj = BlankJSONFieldTestModel.objects.create(blank_json='', null_json=None)
+        self.assertEquals('', obj.blank_json)
+        self.assertEquals(None, obj.null_json)

jsonfield/tests/test_forms.py

 from django.test import TestCase as DjangoTestCase
 from django.utils import unittest
+from django.forms import ValidationError
 
 from jsonfield.forms import JSONFormField
 from jsonfield.tests.jsonfield_test_app.forms import JSONTestForm, JSONTestModelForm
 
 class JSONFormFieldTest(DjangoTestCase):
-    def test_form_field_clean(self):
+    def test_form_field_clean_empty_object(self):
         field = JSONFormField(required=False)
         self.assertEquals({}, field.clean('{}'))
         
+    def test_form_field_clean_object(self):
+        field = JSONFormField(required=False)
         self.assertEquals(
             {'foo':'bar', 'baz':2},
             field.clean('{"foo":"bar","baz":2}')
         )
+    
+    def test_form_field_clean_empty_array(self):
+        field = JSONFormField(required=False)
+        self.assertEquals([],field.clean('[]'))
+    
+    def test_required_form_field_array(self):
+        field = JSONFormField(required=True)
+        self.assertEquals([], field.clean('[]'))
         
-        self.assertEquals([],field.clean('[]'))
+    def test_required_form_field_object(self):
+        field = JSONFormField(required=True)
+        self.assertEquals({}, field.clean('{}'))
+    
+    def test_required_form_field_empty(self):
+        field = JSONFormField(required=True)
+        with self.assertRaises(ValidationError):
+            field.clean('')
+    
+    def test_invalid_json(self):
+        field = JSONFormField(required=True)
+        
+        with self.assertRaises(ValidationError):
+            field.clean('{"foo"}')
 
 class JSONFormTest(DjangoTestCase):
     def test_form_clean(self):
         form = JSONTestForm({})
-        self.assertFalse(form.is_valid())
+        self.assertFalse(form.is_valid())
+    
+
+class JSONFormMultipleSelectFieldTest(DjangoTestCase):
+    def test_multiple_select_data(self):
+        form = JSONTestForm({'json_data': ['SA', 'WA']})
+        assert form.is_valid()
+        
+        self.assertEquals(['SA', 'WA'], form.cleaned_data['json_data'])
+
+

jsonfield/widgets.py

+import json 
+
 from django import forms
-from django.utils import simplejson as json
 from django.conf import settings
 
 from .utils import default
 
 class JSONSelectWidget(forms.SelectMultiple):
     pass
-
         'Framework :: Django',
     ],
     test_suite='tests.main',
+    include_package_data=True,
 )
         test_runner = get_runner(global_settings)
 
     test_runner = test_runner()
+    
+    if getattr(django, 'setup', None):
+        django.setup()
+    
     failures = test_runner.run_tests(['jsonfield'])
     
     sys.exit(failures)
 [tox]
-envlist = py26,py27,py32,pypy,py27-django-1.4,py27-django-1.5,py27-django-1.6,py27-django-trunk
+envlist = py26,py27,py32,py33,pypy,py27-django-1.4,py27-django-1.5,py27-django-1.6,py27-django-trunk
 
 [base]
 deps=
 
 [testenv:py32]
 setenv=
-  COVERAGE=
+  COVERAGE=
+  
+[testenv:py33]
+setenv=
+  COVERAGE=