Loading django/contrib/postgres/forms/jsonb.py +15 −0 Original line number Diff line number Diff line import json from django import forms from django.utils import six from django.utils.translation import ugettext_lazy as _ __all__ = ['JSONField'] class InvalidJSONInput(six.text_type): pass class JSONField(forms.CharField): default_error_messages = { 'invalid': _("'%(value)s' value must be valid JSON."), Loading @@ -27,5 +32,15 @@ class JSONField(forms.CharField): params={'value': value}, ) def bound_data(self, data, initial): if self.disabled: return initial try: return json.loads(data) except ValueError: return InvalidJSONInput(data) def prepare_value(self, value): if isinstance(value, InvalidJSONInput): return value return json.dumps(value) docs/releases/1.9.5.txt +3 −0 Original line number Diff line number Diff line Loading @@ -46,3 +46,6 @@ Bugfixes * Fixed a migrations crash on SQLite when renaming the primary key of a model containing a ``ForeignKey`` to ``'self'`` (:ticket:`26384`). * Fixed ``JSONField`` inadvertently escaping its contents when displaying values after failed form validation (:ticket:`25532`). tests/postgres_tests/test_json.py +29 −0 Original line number Diff line number Diff line Loading @@ -3,7 +3,9 @@ import unittest from django.core import exceptions, serializers from django.db import connection from django.forms import CharField, Form from django.test import TestCase from django.utils.html import escape from . import PostgreSQLTestCase from .models import JSONModel Loading Loading @@ -258,7 +260,34 @@ class TestFormField(PostgreSQLTestCase): form_field = model_field.formfield() self.assertIsInstance(form_field, forms.JSONField) def test_formfield_disabled(self): class JsonForm(Form): name = CharField() jfield = forms.JSONField(disabled=True) form = JsonForm({'name': 'xyz', 'jfield': '["bar"]'}, initial={'jfield': ['foo']}) self.assertIn('["foo"]</textarea>', form.as_p()) def test_prepare_value(self): field = forms.JSONField() self.assertEqual(field.prepare_value({'a': 'b'}), '{"a": "b"}') self.assertEqual(field.prepare_value(None), 'null') self.assertEqual(field.prepare_value('foo'), '"foo"') def test_redisplay_wrong_input(self): """ When displaying a bound form (typically due to invalid input), the form should not overquote JSONField inputs. """ class JsonForm(Form): name = CharField(max_length=2) jfield = forms.JSONField() # JSONField input is fine, name is too long form = JsonForm({'name': 'xyz', 'jfield': '["foo"]'}) self.assertIn('["foo"]</textarea>', form.as_p()) # This time, the JSONField input is wrong form = JsonForm({'name': 'xy', 'jfield': '{"foo"}'}) # Appears once in the textarea and once in the error message self.assertEqual(form.as_p().count(escape('{"foo"}')), 2) Loading
django/contrib/postgres/forms/jsonb.py +15 −0 Original line number Diff line number Diff line import json from django import forms from django.utils import six from django.utils.translation import ugettext_lazy as _ __all__ = ['JSONField'] class InvalidJSONInput(six.text_type): pass class JSONField(forms.CharField): default_error_messages = { 'invalid': _("'%(value)s' value must be valid JSON."), Loading @@ -27,5 +32,15 @@ class JSONField(forms.CharField): params={'value': value}, ) def bound_data(self, data, initial): if self.disabled: return initial try: return json.loads(data) except ValueError: return InvalidJSONInput(data) def prepare_value(self, value): if isinstance(value, InvalidJSONInput): return value return json.dumps(value)
docs/releases/1.9.5.txt +3 −0 Original line number Diff line number Diff line Loading @@ -46,3 +46,6 @@ Bugfixes * Fixed a migrations crash on SQLite when renaming the primary key of a model containing a ``ForeignKey`` to ``'self'`` (:ticket:`26384`). * Fixed ``JSONField`` inadvertently escaping its contents when displaying values after failed form validation (:ticket:`25532`).
tests/postgres_tests/test_json.py +29 −0 Original line number Diff line number Diff line Loading @@ -3,7 +3,9 @@ import unittest from django.core import exceptions, serializers from django.db import connection from django.forms import CharField, Form from django.test import TestCase from django.utils.html import escape from . import PostgreSQLTestCase from .models import JSONModel Loading Loading @@ -258,7 +260,34 @@ class TestFormField(PostgreSQLTestCase): form_field = model_field.formfield() self.assertIsInstance(form_field, forms.JSONField) def test_formfield_disabled(self): class JsonForm(Form): name = CharField() jfield = forms.JSONField(disabled=True) form = JsonForm({'name': 'xyz', 'jfield': '["bar"]'}, initial={'jfield': ['foo']}) self.assertIn('["foo"]</textarea>', form.as_p()) def test_prepare_value(self): field = forms.JSONField() self.assertEqual(field.prepare_value({'a': 'b'}), '{"a": "b"}') self.assertEqual(field.prepare_value(None), 'null') self.assertEqual(field.prepare_value('foo'), '"foo"') def test_redisplay_wrong_input(self): """ When displaying a bound form (typically due to invalid input), the form should not overquote JSONField inputs. """ class JsonForm(Form): name = CharField(max_length=2) jfield = forms.JSONField() # JSONField input is fine, name is too long form = JsonForm({'name': 'xyz', 'jfield': '["foo"]'}) self.assertIn('["foo"]</textarea>', form.as_p()) # This time, the JSONField input is wrong form = JsonForm({'name': 'xy', 'jfield': '{"foo"}'}) # Appears once in the textarea and once in the error message self.assertEqual(form.as_p().count(escape('{"foo"}')), 2)