diff --git a/django/contrib/postgres/forms/jsonb.py b/django/contrib/postgres/forms/jsonb.py index 83a62a8b7c..719de3ae66 100644 --- a/django/contrib/postgres/forms/jsonb.py +++ b/django/contrib/postgres/forms/jsonb.py @@ -11,6 +11,10 @@ class InvalidJSONInput(six.text_type): pass +class JSONString(six.text_type): + pass + + class JSONField(forms.CharField): default_error_messages = { 'invalid': _("'%(value)s' value must be valid JSON."), @@ -22,14 +26,20 @@ class JSONField(forms.CharField): return value if value in self.empty_values: return None + elif isinstance(value, (list, dict, int, float, JSONString)): + return value try: - return json.loads(value) + converted = json.loads(value) except ValueError: raise forms.ValidationError( self.error_messages['invalid'], code='invalid', params={'value': value}, ) + if isinstance(converted, six.text_type): + return JSONString(converted) + else: + return converted def bound_data(self, data, initial): if self.disabled: diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py index 78dded31a9..9691329a49 100644 --- a/tests/postgres_tests/test_json.py +++ b/tests/postgres_tests/test_json.py @@ -365,3 +365,13 @@ class TestFormField(PostgreSQLTestCase): field = CustomJSONField() self.assertIsInstance(field.widget, widgets.Input) + + def test_already_converted_value(self): + field = forms.JSONField(required=False) + tests = [ + '["a", "b", "c"]', '{"a": 1, "b": 2}', '1', '1.5', '"foo"', + 'true', 'false', 'null', + ] + for json_string in tests: + val = field.clean(json_string) + self.assertEqual(field.clean(val), val)