From 8dea9f089dcb8aea2c417c91344c4e680431e972 Mon Sep 17 00:00:00 2001 From: Greg Chapple Date: Mon, 25 Jan 2016 12:30:40 +0000 Subject: [PATCH] Fixed #26120 -- Made HStoreField cast keys and values to strings. HStoreField now converts all keys and values to string before they're saved to the database. --- django/contrib/postgres/fields/hstore.py | 17 +++++++++++++++++ docs/releases/1.10.txt | 3 ++- tests/postgres_tests/test_hstore.py | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/django/contrib/postgres/fields/hstore.py b/django/contrib/postgres/fields/hstore.py index 1fb9138e9a..8322d8170c 100644 --- a/django/contrib/postgres/fields/hstore.py +++ b/django/contrib/postgres/fields/hstore.py @@ -5,6 +5,7 @@ from django.contrib.postgres.fields.array import ArrayField 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'] @@ -51,6 +52,22 @@ class HStoreField(Field): 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) diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index 1f9cc69453..6568d0ff45 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -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` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/postgres_tests/test_hstore.py b/tests/postgres_tests/test_hstore.py index 80e46e5021..13c08386e2 100644 --- a/tests/postgres_tests/test_hstore.py +++ b/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 @@ -37,6 +40,20 @@ class SimpleTests(PostgreSQLTestCase): 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):