M django/contrib/postgres/forms/jsonb.py => django/contrib/postgres/forms/jsonb.py +15 -0
@@ 1,11 1,16 @@
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."),
@@ 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)
M docs/releases/1.9.5.txt => docs/releases/1.9.5.txt +3 -0
@@ 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`).
M tests/postgres_tests/test_json.py => tests/postgres_tests/test_json.py +29 -0
@@ 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
@@ 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)