mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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.
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -365,13 +365,14 @@ class BaseForm(object): | ||||
|  | ||||
|     def _clean_fields(self): | ||||
|         for name, field in self.fields.items(): | ||||
|             if field.disabled: | ||||
|                 # Initial values are supposed to be clean | ||||
|                 self.cleaned_data[name] = self.initial.get(name, field.initial) | ||||
|                 continue | ||||
|             # value_from_datadict() gets the data from the data dictionaries. | ||||
|             # Each widget type knows how to retrieve its own data, because some | ||||
|             # widgets split data over several HTML fields. | ||||
|             if field.disabled: | ||||
|                 value = self.initial.get(name, field.initial) | ||||
|             else: | ||||
|                 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) | ||||
|             value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) | ||||
|             try: | ||||
|                 if isinstance(field, FileField): | ||||
|                     initial = self.initial.get(name, field.initial) | ||||
|   | ||||
| @@ -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`). | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user