diff options
| author | David Hoffman <me@davidhoffman.ca> | 2016-12-08 19:17:02 -0500 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-12-15 10:35:44 -0500 |
| commit | bf84d042e0bba636fe2199051fe25e96d89865da (patch) | |
| tree | 119470db1a6f0f6d5de6b66f71929c92a6a4e97f | |
| parent | 9524fd9133e47fa90846904f81fe91c72f331e56 (diff) | |
Fixed #27582 -- Allowed HStoreField to store null values.
| -rw-r--r-- | django/contrib/postgres/fields/hstore.py | 6 | ||||
| -rw-r--r-- | django/contrib/postgres/forms/hstore.py | 4 | ||||
| -rw-r--r-- | docs/ref/contrib/postgres/fields.txt | 9 | ||||
| -rw-r--r-- | docs/ref/contrib/postgres/forms.txt | 8 | ||||
| -rw-r--r-- | docs/releases/1.11.txt | 4 | ||||
| -rw-r--r-- | tests/postgres_tests/test_hstore.py | 11 |
6 files changed, 33 insertions, 9 deletions
diff --git a/django/contrib/postgres/fields/hstore.py b/django/contrib/postgres/fields/hstore.py index 76f41886a9..605deaf62c 100644 --- a/django/contrib/postgres/fields/hstore.py +++ b/django/contrib/postgres/fields/hstore.py @@ -13,9 +13,9 @@ __all__ = ['HStoreField'] class HStoreField(Field): empty_strings_allowed = False - description = _('Map of strings to strings') + description = _('Map of strings to strings/nulls') default_error_messages = { - 'not_a_string': _('The value of "%(key)s" is not a string.'), + 'not_a_string': _('The value of "%(key)s" is not a string or null.'), } def db_type(self, connection): @@ -30,7 +30,7 @@ class HStoreField(Field): def validate(self, value, model_instance): super(HStoreField, self).validate(value, model_instance) for key, val in value.items(): - if not isinstance(val, six.string_types): + if not isinstance(val, six.string_types) and val is not None: raise exceptions.ValidationError( self.error_messages['not_a_string'], code='not_a_string', diff --git a/django/contrib/postgres/forms/hstore.py b/django/contrib/postgres/forms/hstore.py index 4ed99d1fda..76d362999d 100644 --- a/django/contrib/postgres/forms/hstore.py +++ b/django/contrib/postgres/forms/hstore.py @@ -43,7 +43,9 @@ class HStoreField(forms.CharField): # Cast everything to strings for ease. for key, val in value.items(): - value[key] = six.text_type(val) + if val is not None: + val = six.text_type(val) + value[key] = val return value def has_changed(self, initial, data): diff --git a/docs/ref/contrib/postgres/fields.txt b/docs/ref/contrib/postgres/fields.txt index 7c756655ce..f753f3b75e 100644 --- a/docs/ref/contrib/postgres/fields.txt +++ b/docs/ref/contrib/postgres/fields.txt @@ -275,8 +275,9 @@ A more useful index is a ``GIN`` index, which you should create using a .. class:: HStoreField(**options) - A field for storing mappings of strings to strings. The Python data type - used is a ``dict``. + A field for storing key-value pairs. The Python data type used is a + ``dict``. Keys must be strings, and values may be either strings or nulls + (``None`` in Python). To use this field, you'll need to: @@ -287,6 +288,10 @@ A more useful index is a ``GIN`` index, which you should create using a You'll see an error like ``can't adapt type 'dict'`` if you skip the first step, or ``type "hstore" does not exist`` if you skip the second. + .. versionchanged:: 1.11 + + Added the ability to store nulls. Previously, they were cast to strings. + .. note:: On occasions it may be useful to require or restrict the keys which are diff --git a/docs/ref/contrib/postgres/forms.txt b/docs/ref/contrib/postgres/forms.txt index c4af819a72..bfc7aa3e7a 100644 --- a/docs/ref/contrib/postgres/forms.txt +++ b/docs/ref/contrib/postgres/forms.txt @@ -144,8 +144,8 @@ Fields .. class:: HStoreField A field which accepts JSON encoded data for an - :class:`~django.contrib.postgres.fields.HStoreField`. It will cast all the - values to strings. It is represented by an HTML ``<textarea>``. + :class:`~django.contrib.postgres.fields.HStoreField`. It casts all values + (except nulls) to strings. It is represented by an HTML ``<textarea>``. .. admonition:: User friendly forms @@ -159,6 +159,10 @@ Fields valid for a given field. This can be done using the :class:`~django.contrib.postgres.validators.KeysValidator`. + .. versionchanged:: 1.11 + + Added the ability to store nulls. + ``JSONField`` ------------- diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index e14d76ca1f..a46edbdfb9 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -194,6 +194,10 @@ Minor features * The new :class:`~django.contrib.postgres.aggregates.JSONBAgg` allows aggregating values as a JSON array. +* :class:`~django.contrib.postgres.fields.HStoreField` and + :class:`~django.contrib.postgres.forms.HStoreField` allow storing null + values. + :mod:`django.contrib.redirects` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/postgres_tests/test_hstore.py b/tests/postgres_tests/test_hstore.py index fef71f1ef4..0fc427f67c 100644 --- a/tests/postgres_tests/test_hstore.py +++ b/tests/postgres_tests/test_hstore.py @@ -195,7 +195,11 @@ class TestValidation(HStoreTestCase): with self.assertRaises(exceptions.ValidationError) as cm: field.clean({'a': 1}, None) self.assertEqual(cm.exception.code, 'not_a_string') - self.assertEqual(cm.exception.message % cm.exception.params, 'The value of "a" is not a string.') + self.assertEqual(cm.exception.message % cm.exception.params, 'The value of "a" is not a string or null.') + + def test_none_allowed_as_value(self): + field = HStoreField() + self.assertEqual(field.clean({'a': None}, None), {'a': None}) class TestFormField(HStoreTestCase): @@ -224,6 +228,11 @@ class TestFormField(HStoreTestCase): value = field.clean('{"a": 1}') self.assertEqual(value, {'a': '1'}) + def test_none_value(self): + field = forms.HStoreField() + value = field.clean('{"a": null}') + self.assertEqual(value, {'a': None}) + def test_empty(self): field = forms.HStoreField(required=False) value = field.clean('') |
