Skip to content

Commit

Permalink
Fixed #26120 -- Made HStoreField cast keys and values to strings.
Browse files Browse the repository at this point in the history
HStoreField now converts all keys and values to string before they're
saved to the database.
  • Loading branch information
gchp authored and timgraham committed Jan 29, 2016
1 parent 93897a6 commit 8dea9f0
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 1 deletion.
17 changes: 17 additions & 0 deletions django/contrib/postgres/fields/hstore.py
Expand Up @@ -5,6 +5,7 @@
from django.core import exceptions
from django.db.models import Field, TextField, Transform
from django.utils import six
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _

__all__ = ['HStoreField']
Expand Down Expand Up @@ -51,6 +52,22 @@ def formfield(self, **kwargs):
defaults.update(kwargs)
return super(HStoreField, self).formfield(**defaults)

def get_prep_value(self, value):
value = super(HStoreField, self).get_prep_value(value)

if isinstance(value, dict):
prep_value = {}
for key, val in value.items():
key = force_text(key)
if val is not None:
val = force_text(val)
prep_value[key] = val
value = prep_value

if isinstance(value, list):
value = [force_text(item) for item in value]

return value

HStoreField.register_lookup(lookups.DataContains)
HStoreField.register_lookup(lookups.ContainedBy)
Expand Down
3 changes: 2 additions & 1 deletion docs/releases/1.10.txt
Expand Up @@ -129,7 +129,8 @@ Minor features
:mod:`django.contrib.postgres`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* ...
* For convenience, :class:`~django.contrib.postgres.fields.HStoreField` now
casts its keys and values to strings.

:mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
17 changes: 17 additions & 0 deletions tests/postgres_tests/test_hstore.py
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import json

from django.core import exceptions, serializers
Expand Down Expand Up @@ -37,6 +40,20 @@ def test_value_null(self):
reloaded = HStoreModel.objects.get()
self.assertEqual(reloaded.field, value)

def test_key_val_cast_to_string(self):
value = {'a': 1, 'b': 'B', 2: 'c', 'ï': 'ê', b'x': b'test'}
expected_value = {'a': '1', 'b': 'B', '2': 'c', 'ï': 'ê', 'x': 'test'}

instance = HStoreModel.objects.create(field=value)
instance = HStoreModel.objects.get()
self.assertDictEqual(instance.field, expected_value)

instance = HStoreModel.objects.get(field__a=1)
self.assertDictEqual(instance.field, expected_value)

instance = HStoreModel.objects.get(field__has_keys=[2, 'a', 'ï'])
self.assertDictEqual(instance.field, expected_value)


class TestQuerying(PostgreSQLTestCase):

Expand Down

0 comments on commit 8dea9f0

Please sign in to comment.