summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hoffman <me@davidhoffman.ca>2016-12-08 19:17:02 -0500
committerTim Graham <timograham@gmail.com>2016-12-15 10:35:44 -0500
commitbf84d042e0bba636fe2199051fe25e96d89865da (patch)
tree119470db1a6f0f6d5de6b66f71929c92a6a4e97f
parent9524fd9133e47fa90846904f81fe91c72f331e56 (diff)
Fixed #27582 -- Allowed HStoreField to store null values.
-rw-r--r--django/contrib/postgres/fields/hstore.py6
-rw-r--r--django/contrib/postgres/forms/hstore.py4
-rw-r--r--docs/ref/contrib/postgres/fields.txt9
-rw-r--r--docs/ref/contrib/postgres/forms.txt8
-rw-r--r--docs/releases/1.11.txt4
-rw-r--r--tests/postgres_tests/test_hstore.py11
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('')