~turminal/django

6a8ba2eef45ccc3022c88510bffd29734d71c39e — Claude Paroz 8 years ago a304e7d
[1.9.x] Fixed #25532 -- Properly redisplayed JSONField form input values

Thanks David Szotten for the report and Tommy Beadle for code inspiration.
Thanks Tim Graham for the review.
Partial backport of db19619545 from master.
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('[&quot;foo&quot;]</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('[&quot;foo&quot;]</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)