From 75b6ccf86667ff3ec0919fbf67ff421aa49b34bc Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Tue, 23 Sep 2014 12:21:19 +0200 Subject: [PATCH] Fallback default value of an HStoreField is an empty HStoreDict #69 --- django_hstore/__init__.py | 2 +- django_hstore/fields.py | 30 +++++++++++++----------------- tests/django_hstore_tests/tests.py | 27 ++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/django_hstore/__init__.py b/django_hstore/__init__.py index a791fd1..fb5cb8f 100755 --- a/django_hstore/__init__.py +++ b/django_hstore/__init__.py @@ -1,4 +1,4 @@ -VERSION = (1, 3, 2, 'final') +VERSION = (1, 3, 3, 'alpha') __version__ = VERSION diff --git a/django_hstore/fields.py b/django_hstore/fields.py index 3139cd7..926f525 100755 --- a/django_hstore/fields.py +++ b/django_hstore/fields.py @@ -13,7 +13,7 @@ class HStoreField(models.Field): """ HStore Base Field """ - + def __init_dict(self, value): """ initializes HStoreDict @@ -42,10 +42,6 @@ def get_default(self): return self.__init_dict(self.default) # else just return it return self.default - # if allowed to return None - if (not self.empty_strings_allowed or (self.null and - not connection.features.interprets_empty_strings_as_nulls)): - return None # default to empty dict return self.__init_dict({}) @@ -86,11 +82,11 @@ def south_field_triple(self): class DictionaryField(HStoreField): description = _("A python dictionary in a postgresql hstore field.") - + def __init__(self, *args, **kwargs): self.schema = kwargs.pop('schema', None) self.schema_mode = False - + # if schema parameter is supplied the behaviour is slightly different if self.schema is not None: # schema mode available only from django 1.6 onward @@ -104,9 +100,9 @@ def __init__(self, *args, **kwargs): kwargs['default'] = kwargs.get('default', {}) # null defaults to True to facilitate migrations kwargs['null'] = kwargs.get('null', True) - + super(DictionaryField, self).__init__(*args, **kwargs) - + def __init_dict(self, value): """ init HStoreDict @@ -117,24 +113,24 @@ def __init_dict(self, value): def contribute_to_class(self, cls, name): super(DictionaryField, self).contribute_to_class(cls, name) setattr(cls, self.name, HStoreDescriptor(self, schema_mode=self.schema_mode)) - + if self.schema: self._create_hstore_virtual_fields(cls, name) - + def _validate_schema(self, schema): if not isinstance(schema, list): raise ValueError('schema parameter must be a list') - + if len(schema) == 0: raise ValueError('schema parameter cannot be an empty list') - + for field in schema: if not isinstance(field, dict): raise ValueError('schema parameter must contain dicts representing fields, read the docs to see the format') - + if 'name' not in field: raise ValueError('schema element %s is missing the name key' % field) - + if 'class' not in field: raise ValueError('schema element %s is missing the class key' % field) @@ -145,7 +141,7 @@ def _create_hstore_virtual_fields(self, cls, hstore_field_name): # add hstore_virtual_fields attribute to class if not hasattr(cls, '_hstore_virtual_fields'): cls._hstore_virtual_fields = {} - + for field in self.schema: # initialize the virtual field by specifying the class, the kwargs and the hstore field name virtual_field = create_hstore_virtual_field(field['class'], @@ -162,7 +158,7 @@ def formfield(self, **kwargs): def _value_to_python(self, value): return value - + def south_field_triple(self): name, args, kwargs = super(DictionaryField, self).south_field_triple() # if schema mode replace the default value {} with None as {} would break south diff --git a/tests/django_hstore_tests/tests.py b/tests/django_hstore_tests/tests.py index 02a5f09..a6fac69 100755 --- a/tests/django_hstore_tests/tests.py +++ b/tests/django_hstore_tests/tests.py @@ -128,11 +128,29 @@ def test_empty_querying(self): self.assertTrue(DataBag.objects.filter(data__contains={})) def test_nullable_queryinig(self): + # backward incompatible change in 1.3.3: + # default value of a dictionary field which is can be null will never be None + # but always an empty HStoreDict NullableDataBag.objects.create(name='nullable') - self.assertTrue(NullableDataBag.objects.get(data=None)) - self.assertTrue(NullableDataBag.objects.filter(data__exact=None)) - self.assertTrue(NullableDataBag.objects.filter(data__isnull=True)) - self.assertFalse(NullableDataBag.objects.filter(data__isnull=False)) + self.assertFalse(NullableDataBag.objects.filter(data__exact=None)) + self.assertFalse(NullableDataBag.objects.filter(data__isnull=True)) + self.assertTrue(NullableDataBag.objects.filter(data__isnull=False)) + + def test_nullable_set(self): + n = NullableDataBag() + n.data['test'] = 'test' + self.assertEqual(n.data['test'], 'test') + + def test_nullable_get(self): + n = NullableDataBag() + self.assertEqual(n.data.get('test', 'test'), 'test') + self.assertEqual(n.data.get('test', False), False) + self.assertEqual(n.data.get('test'), None) + + def test_nullable_getitem(self): + n = NullableDataBag() + with self.assertRaises(KeyError): + n.data['test'] def test_named_querying(self): alpha, beta = self._create_bags() @@ -823,7 +841,6 @@ def test_basefield_attribute(self): def test_str(self): d = SchemaDataBag() self.assertEqual(str(d.data), '{}') - else: def test_improperly_configured(self): with self.assertRaises(ImproperlyConfigured):