Skip to content

Commit

Permalink
Fallback default value of an HStoreField is an empty HStoreDict #69
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier committed Sep 24, 2014
1 parent 24e4b91 commit 75b6ccf
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 23 deletions.
2 changes: 1 addition & 1 deletion django_hstore/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = (1, 3, 2, 'final')
VERSION = (1, 3, 3, 'alpha')
__version__ = VERSION


Expand Down
30 changes: 13 additions & 17 deletions django_hstore/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class HStoreField(models.Field):
""" HStore Base Field """

def __init_dict(self, value):
"""
initializes HStoreDict
Expand Down Expand Up @@ -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({})

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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'],
Expand All @@ -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
Expand Down
27 changes: 22 additions & 5 deletions tests/django_hstore_tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit 75b6ccf

Please sign in to comment.