From 5e403bf469ac8fe8e4c3d9d08f05c79e47662296 Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Mon, 29 Jun 2015 16:18:15 +0200 Subject: [PATCH] Split tests in multiple files + cleanup * split tests in multiple files for clarity * eliminated redundant (copy & paste) tests * ensured tests belong to what is being tested (had to move few tests between the suites) --- tests/django_hstore_tests/tests.py | 2014 ----------------- tests/django_hstore_tests/tests/__init__.py | 0 .../tests/test_dictionary_field.py | 688 ++++++ .../tests/test_gis_integration.py | 73 + .../tests/test_not_transactional.py | 39 + .../tests/test_reference_field.py | 192 ++ .../tests/test_schema_mode.py | 459 ++++ .../tests/test_serialized_dictionary_field.py | 460 ++++ 8 files changed, 1911 insertions(+), 2014 deletions(-) delete mode 100644 tests/django_hstore_tests/tests.py create mode 100644 tests/django_hstore_tests/tests/__init__.py create mode 100644 tests/django_hstore_tests/tests/test_dictionary_field.py create mode 100644 tests/django_hstore_tests/tests/test_gis_integration.py create mode 100644 tests/django_hstore_tests/tests/test_not_transactional.py create mode 100644 tests/django_hstore_tests/tests/test_reference_field.py create mode 100644 tests/django_hstore_tests/tests/test_schema_mode.py create mode 100644 tests/django_hstore_tests/tests/test_serialized_dictionary_field.py diff --git a/tests/django_hstore_tests/tests.py b/tests/django_hstore_tests/tests.py deleted file mode 100644 index a63cf36..0000000 --- a/tests/django_hstore_tests/tests.py +++ /dev/null @@ -1,2014 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import sys -import json -import pickle -from decimal import Decimal -import datetime - -from django import VERSION as DJANGO_VERSION -from django import forms -from django.db import transaction, connection, models -from django.db.models.aggregates import Count -from django.db.utils import IntegrityError -from django.core.exceptions import ValidationError, ImproperlyConfigured -from django.core.urlresolvers import reverse -from django.contrib.auth.models import User -from django.utils.encoding import force_text -from django.test import TestCase -from django.test import SimpleTestCase - -from django_hstore import get_version, hstore -from django_hstore.apps import GEODJANGO_INSTALLED -from django_hstore.forms import DictionaryFieldWidget, ReferencesFieldWidget, SerializedDictionaryFieldWidget -from django_hstore.fields import HStoreDict -from django_hstore.exceptions import HStoreDictException -from django_hstore.utils import unserialize_references, serialize_references, acquire_reference, get_cast_for_param -from django_hstore.virtual import create_hstore_virtual_field - -from django_hstore_tests.models import * # noqa - - -class TestDictionaryField(TestCase): - def setUp(self): - DataBag.objects.all().delete() - Ref.objects.all().delete() - RefsBag.objects.all().delete() - - def _create_bags(self): - alpha = DataBag.objects.create(name='alpha', data={'v': '1', 'v2': '3'}) - beta = DataBag.objects.create(name='beta', data={'v': '2', 'v2': '4'}) - return alpha, beta - - def _create_bitfield_bags(self): - # create dictionaries with bits as dictionary keys (i.e. bag5 = {'b0':'1', 'b2':'1'}) - for i in range(10): - DataBag.objects.create(name='bag%d' % (i,), - data=dict(('b%d' % (bit,), '1') for bit in range(4) if (1 << bit) & i)) - - def test_hstore_dict(self): - alpha, beta = self._create_bags() - self.assertEqual(alpha.data, {'v': '1', 'v2': '3'}) - self.assertEqual(beta.data, {'v': '2', 'v2': '4'}) - - def test_decimal(self): - databag = DataBag(name='decimal') - databag.data['dec'] = Decimal('1.01') - self.assertEqual(databag.data['dec'], force_text(Decimal('1.01'))) - - databag.save() - databag = DataBag.objects.get(name='decimal') - self.assertEqual(databag.data['dec'], force_text(Decimal('1.01'))) - - databag = DataBag(name='decimal', data={'dec': Decimal('1.01')}) - self.assertEqual(databag.data['dec'], force_text(Decimal('1.01'))) - - def test_long(self): - if sys.version < '3': - l = long(100000000000) - databag = DataBag(name='long') - databag.data['long'] = l - self.assertEqual(databag.data['long'], force_text(l)) - - databag.save() - databag = DataBag.objects.get(name='long') - self.assertEqual(databag.data['long'], force_text(l)) - - databag = DataBag(name='long', data={'long': l}) - self.assertEqual(databag.data['long'], force_text(l)) - - def test_number(self): - databag = DataBag(name='number') - databag.data['num'] = 1 - self.assertEqual(databag.data['num'], '1') - - databag.save() - databag = DataBag.objects.get(name='number') - self.assertEqual(databag.data['num'], '1') - - databag = DataBag(name='number', data={'num': 1}) - self.assertEqual(databag.data['num'], '1') - - def test_list(self): - databag = DataBag.objects.create(name='list', data={'list': ['a', 'b', 'c']}) - databag = DataBag.objects.get(name='list') - self.assertEqual(json.loads(databag.data['list']), ['a', 'b', 'c']) - - def test_dictionary(self): - databag = DataBag.objects.create(name='dict', data={'dict': {'subkey': 'subvalue'}}) - databag = DataBag.objects.get(name='dict') - self.assertEqual(json.loads(databag.data['dict']), {'subkey': 'subvalue'}) - - databag.data['dict'] = {'subkey': True, 'list': ['a', 'b', False]} - databag.save() - self.assertEqual(json.loads(databag.data['dict']), {'subkey': True, 'list': ['a', 'b', False]}) - - def test_boolean(self): - databag = DataBag.objects.create(name='boolean', data={'boolean': True}) - databag = DataBag.objects.get(name='boolean') - self.assertEqual(json.loads(databag.data['boolean']), True) - - self.assertTrue(DataBag.objects.get(data__contains={'boolean': True})) - - def test_is_pickable(self): - m = DefaultsModel() - m.save() - try: - pickle.dumps(m) - except TypeError as e: - self.fail('pickle of DefaultsModel failed: %s' % e) - - def test_empty_instantiation(self): - bag = DataBag.objects.create(name='bag') - self.assertTrue(isinstance(bag.data, dict)) - self.assertEqual(bag.data, {}) - - def test_empty_querying(self): - DataBag.objects.create(name='bag') - self.assertTrue(DataBag.objects.get(data={})) - self.assertTrue(DataBag.objects.filter(data={})) - 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.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_null_values(self): - null_v = DataBag.objects.create(name="test", data={"v": None}) - nonnull_v = DataBag.objects.create(name="test", data={"v": "item"}) - - r = DataBag.objects.filter(data__isnull={"v": True}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], null_v) - - r = DataBag.objects.filter(data__isnull={"v": False}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], nonnull_v) - - def test_named_querying(self): - alpha, beta = self._create_bags() - self.assertEqual(DataBag.objects.get(name='alpha'), alpha) - self.assertEqual(DataBag.objects.filter(name='beta')[0], beta) - - def test_aggregates(self): - self._create_bitfield_bags() - - self.assertEqual(DataBag.objects.filter(data__contains={'b0': '1'}).aggregate(Count('id'))['id__count'], 5) - self.assertEqual(DataBag.objects.filter(data__contains={'b1': '1'}).aggregate(Count('id'))['id__count'], 4) - - def test_annotations(self): - self._create_bitfield_bags() - - self.assertEqual(DataBag.objects.annotate(num_id=Count('id')).filter(num_id=1)[0].num_id, 1) - - def test_nested_filtering(self): - self._create_bitfield_bags() - - # Test cumulative successive filters for both dictionaries and other fields - f = DataBag.objects.all() - self.assertEqual(10, f.count()) - f = f.filter(data__contains={'b0': '1'}) - self.assertEqual(5, f.count()) - f = f.filter(data__contains={'b1': '1'}) - self.assertEqual(2, f.count()) - f = f.filter(name='bag3') - self.assertEqual(1, f.count()) - - def test_unicode_processing(self): - greets = { - u'de': u'Gr\xfc\xdfe, Welt', - u'en': u'hello, world', - u'es': u'hola, ma\xf1ana', - u'he': u'\u05e9\u05dc\u05d5\u05dd, \u05e2\u05d5\u05dc\u05dd', - u'jp': u'\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c', - u'zh': u'\u4f60\u597d\uff0c\u4e16\u754c', - } - DataBag.objects.create(name='multilang', data=greets) - self.assertEqual(greets, DataBag.objects.get(name='multilang').data) - - def test_query_escaping(self): - me = self - - def readwrite(s): - # try create and query with potentially illegal characters in the field and dictionary key/value - o = DataBag.objects.create(name=s, data={s: s}) - me.assertEqual(o, DataBag.objects.get(name=s, data={s: s})) - readwrite('\' select') - readwrite('% select') - readwrite('\\\' select') - readwrite('-- select') - readwrite('\n select') - readwrite('\r select') - readwrite('* select') - - def test_replace_full_dictionary(self): - DataBag.objects.create(name='foo', data={'change': 'old value', 'remove': 'baz'}) - - replacement = {'change': 'new value', 'added': 'new'} - DataBag.objects.filter(name='foo').update(data=replacement) - self.assertEqual(replacement, DataBag.objects.get(name='foo').data) - - def test_equivalence_querying(self): - alpha, beta = self._create_bags() - for bag in (alpha, beta): - data = {'v': bag.data['v'], 'v2': bag.data['v2']} - self.assertEqual(DataBag.objects.get(data=data), bag) - r = DataBag.objects.filter(data=data) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - - def test_key_value_subset_querying(self): - alpha, beta = self._create_bags() - for bag in (alpha, beta): - r = DataBag.objects.filter(data__contains={'v': bag.data['v']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - r = DataBag.objects.filter(data__contains={'v': bag.data['v'], 'v2': bag.data['v2']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - - def test_value_in_subset_querying(self): - alpha, beta = self._create_bags() - res = DataBag.objects.filter(data__contains={'v': [alpha.data['v']]}) - self.assertEqual(len(res), 1) - self.assertEqual(res[0], alpha) - res = DataBag.objects.filter(data__contains={'v': [alpha.data['v'], beta.data['v']]}) - self.assertEqual(len(res), 2) - self.assertEqual(set(res), set([alpha, beta])) - - # int values are ok - r = DataBag.objects.filter(data__contains={'v': [int(alpha.data['v'])]}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_key_value_gt_querying(self): - alpha, beta = self._create_bags() - self.assertGreater(beta.data['v'], alpha.data['v']) - r = DataBag.objects.filter(data__gt={'v': alpha.data['v']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], beta) - r = DataBag.objects.filter(data__gte={'v': alpha.data['v']}) - self.assertEqual(len(r), 2) - - def test_key_value_gt_casting_number_query(self): - alpha = DataBag.objects.create(name='alpha', data={'v': 10}) - DataBag.objects.create(name='alpha', data={'v': 1}) - - r = DataBag.objects.filter(data__gt={'v': 2}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_key_value_contains_casting_date_query(self): - date = datetime.date(2014, 9, 28) - alpha = DataBag.objects.create(name='alpha', data={'v': date.isoformat()}) - - r = DataBag.objects.filter(data__contains={'v': date}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_multiple_key_value_gt_querying(self): - alpha, beta = self._create_bags() - self.assertGreater(beta.data['v'], alpha.data['v']) - r = DataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v2': alpha.data['v2']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], beta) - r = DataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v2': beta.data['v2']}) - self.assertEqual(len(r), 0) - r = DataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v2': alpha.data['v2']}) - self.assertEqual(len(r), 2) - r = DataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v2': beta.data['v2']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], beta) - - def test_multiple_key_value_lt_querying(self): - alpha, beta = self._create_bags() - self.assertGreater(beta.data['v'], alpha.data['v']) - r = DataBag.objects.filter(data__lt={'v': beta.data['v'], 'v2': beta.data['v2']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - r = DataBag.objects.filter(data__lt={'v': beta.data['v'], 'v2': alpha.data['v2']}) - self.assertEqual(len(r), 0) - r = DataBag.objects.filter(data__lte={'v': beta.data['v'], 'v2': beta.data['v2']}) - self.assertEqual(len(r), 2) - r = DataBag.objects.filter(data__lte={'v': beta.data['v'], 'v2': alpha.data['v2']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_key_value_lt_querying(self): - alpha, beta = self._create_bags() - self.assertLess(alpha.data['v'], beta.data['v']) - r = DataBag.objects.filter(data__lt={'v': beta.data['v']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - r = DataBag.objects.filter(data__lte={'v': beta.data['v']}) - self.assertEqual(len(r), 2) - - def test_multiple_key_subset_querying(self): - alpha, beta = self._create_bags() - for keys in (['v'], ['v', 'v2']): - self.assertEqual(DataBag.objects.filter(data__contains=keys).count(), 2) - for keys in (['v', 'nv'], ['n1', 'n2']): - self.assertEqual(DataBag.objects.filter(data__contains=keys).count(), 0) - - def test_single_key_querying(self): - alpha, beta = self._create_bags() - for key in ('v', 'v2'): - self.assertEqual(DataBag.objects.filter(data__contains=[key]).count(), 2) - for key in ('n1', 'n2'): - self.assertEqual(DataBag.objects.filter(data__contains=[key]).count(), 0) - - def test_simple_text_icontains_querying(self): - alpha, beta = self._create_bags() - DataBag.objects.create(name='gamma', data={'theKey': 'someverySpecialValue', 'v2': '3'}) - - self.assertEqual(DataBag.objects.filter(data__contains='very').count(), 1) - self.assertEqual(DataBag.objects.filter(data__contains='very')[0].name, 'gamma') - self.assertEqual(DataBag.objects.filter(data__icontains='specialvalue').count(), 1) - self.assertEqual(DataBag.objects.filter(data__icontains='specialvalue')[0].name, 'gamma') - - self.assertEqual(DataBag.objects.filter(data__contains='the').count(), 1) - self.assertEqual(DataBag.objects.filter(data__contains='the')[0].name, 'gamma') - self.assertEqual(DataBag.objects.filter(data__icontains='eke').count(), 1) - self.assertEqual(DataBag.objects.filter(data__icontains='eke')[0].name, 'gamma') - - def test_invalid_containment_lookup_values(self): - alpha, beta = self._create_bags() - with self.assertRaises(ValueError): - DataBag.objects.filter(data__contains=99)[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__icontains=99)[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__icontains=[])[0] - - def test_invalid_comparison_lookup_values(self): - alpha, beta = self._create_bags() - with self.assertRaises(ValueError): - DataBag.objects.filter(data__lt=[1, 2])[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__lt=99)[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__lte=[1, 2])[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__lte=99)[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__gt=[1, 2])[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__gt=99)[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__gte=[1, 2])[0] - with self.assertRaises(ValueError): - DataBag.objects.filter(data__gte=99)[0] - - def test_hkeys(self): - alpha, beta = self._create_bags() - self.assertEqual(DataBag.objects.hkeys(id=alpha.id, attr='data'), ['v', 'v2']) - self.assertEqual(DataBag.objects.hkeys(id=beta.id, attr='data'), ['v', 'v2']) - - def test_hpeek(self): - alpha, beta = self._create_bags() - self.assertEqual(DataBag.objects.hpeek(id=alpha.id, attr='data', key='v'), '1') - self.assertEqual(DataBag.objects.filter(id=alpha.id).hpeek(attr='data', key='v'), '1') - self.assertEqual(DataBag.objects.hpeek(id=alpha.id, attr='data', key='invalid'), None) - - def test_hremove(self): - alpha, beta = self._create_bags() - self.assertEqual(DataBag.objects.get(name='alpha').data, alpha.data) - DataBag.objects.filter(name='alpha').hremove('data', 'v2') - self.assertEqual(DataBag.objects.get(name='alpha').data, {'v': '1'}) - - self.assertEqual(DataBag.objects.get(name='beta').data, beta.data) - DataBag.objects.filter(name='beta').hremove('data', ['v', 'v2']) - self.assertEqual(DataBag.objects.get(name='beta').data, {}) - - def test_hslice(self): - alpha, beta = self._create_bags() - self.assertEqual(DataBag.objects.hslice(id=alpha.id, attr='data', keys=['v']), {'v': '1'}) - self.assertEqual(DataBag.objects.filter(id=alpha.id).hslice(attr='data', keys=['v']), {'v': '1'}) - self.assertEqual(DataBag.objects.hslice(id=alpha.id, attr='data', keys=['ggg']), {}) - - def test_hupdate(self): - alpha, beta = self._create_bags() - self.assertEqual(DataBag.objects.get(name='alpha').data, alpha.data) - DataBag.objects.filter(name='alpha').hupdate('data', {'v2': '10', 'v3': '20'}) - self.assertEqual(DataBag.objects.get(name='alpha').data, {'v': '1', 'v2': '10', 'v3': '20'}) - - def test_hupdate_atomic(self): - """ https://github.com/djangonauts/django-hstore/issues/84 """ - if hasattr(transaction, 'atomic'): - with transaction.atomic(): - self.test_hupdate() - - def test_default(self): - m = DefaultsModel() - m.save() - - def test_bad_default(self): - m = BadDefaultsModel() - try: - m.save() - except IntegrityError: - if DJANGO_VERSION[:2] >= (1, 6): - pass - # TODO: remove in future versions of django-hstore - else: - transaction.rollback() - else: - self.assertTrue(False) - - def test_serialization_deserialization(self): - alpha, beta = self._create_bags() - self.assertEqual(json.loads(str(DataBag.objects.get(name='alpha').data)), json.loads(str(alpha.data))) - self.assertEqual(json.loads(str(DataBag.objects.get(name='beta').data)), json.loads(str(beta.data))) - - def test_hstoredictionaryexception(self): - # ok - HStoreDict({}) - - # json object string allowed - HStoreDict('{}') - - # None is ok, will be converted to empty dict - HStoreDict(None) - HStoreDict() - - # non-json string not allowed - with self.assertRaises(HStoreDictException): - HStoreDict('wrong') - - # list not allowed - with self.assertRaises(HStoreDictException): - HStoreDict(['wrong']) - - # json array string representation not allowed - with self.assertRaises(HStoreDictException): - HStoreDict('["wrong"]') - - # number not allowed - with self.assertRaises(HStoreDictException): - HStoreDict(3) - - def test_hstoredictionary_unicode_vs_str(self): - d = HStoreDict({'test': 'test'}) - self.assertEqual(d.__str__(), d.__unicode__()) - - def test_hstore_model_field_validation(self): - d = DataBag() - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = 'test' - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = '["test"]' - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = ["test"] - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = { - 'a': 1, - 'b': 2.2, - 'c': ['a', 'b'], - 'd': {'test': 'test'} - } - - with self.assertRaises(ValidationError): - d.full_clean() - - def test_admin_widget(self): - alpha, beta = self._create_bags() - - # create admin user - admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) - admin.set_password('tester') - admin.save() - # login as admin - self.client.login(username='admin', password='tester') - - # access admin change form page - url = reverse('admin:django_hstore_tests_databag_change', args=[alpha.id]) - response = self.client.get(url) - # ensure textarea with id="id_data" is there - self.assertContains(response, 'textarea') - self.assertContains(response, 'id_data') - - def test_dictionary_default_admin_widget(self): - class HForm(forms.ModelForm): - class Meta: - model = DataBag - exclude = [] - - form = HForm() - self.assertEqual(form.fields['data'].widget.__class__, DictionaryFieldWidget) - - def test_dictionary_custom_admin_widget(self): - class CustomWidget(forms.Widget): - pass - - class HForm(forms.ModelForm): - class Meta: - model = DataBag - widgets = {'data': CustomWidget} - exclude = [] - - form = HForm() - self.assertEqual(form.fields['data'].widget.__class__, CustomWidget) - - def test_references_default_admin_widget(self): - class HForm(forms.ModelForm): - class Meta: - model = RefsBag - exclude = [] - - form = HForm() - self.assertEqual(form.fields['refs'].widget.__class__, ReferencesFieldWidget) - - def test_references_custom_admin_widget(self): - class CustomWidget(forms.Widget): - pass - - class HForm(forms.ModelForm): - class Meta: - model = RefsBag - widgets = {'refs': CustomWidget} - exclude = [] - - form = HForm() - self.assertEqual(form.fields['refs'].widget.__class__, CustomWidget) - - def test_get_version(self): - get_version() - - def test_unique_together(self): - d = UniqueTogetherDataBag() - d.name = 'test' - d.data = {'test': 'test '} - d.full_clean() - d.save() - - d = UniqueTogetherDataBag() - d.name = 'test' - d.data = {'test': 'test '} - with self.assertRaises(ValidationError): - d.full_clean() - - def test_properties_hstore(self): - """ - Make sure the hstore field does what it is supposed to. - """ - from django_hstore.fields import HStoreDict - - instance = DataBag() - test_props = {'foo': 'bar', 'size': '3'} - - instance.name = 'foo' - instance.data = test_props - instance.save() - - self.assertEqual(type(instance.data), HStoreDict) - self.assertEqual(instance.data, test_props) - instance = DataBag.objects.get(pk=instance.pk) - - self.assertEqual(type(instance.data), HStoreDict) - - self.assertEqual(instance.data, test_props) - self.assertEqual(instance.data['size'], '3') - self.assertIn('foo', instance.data) - - def test_unicode(self): - i = DataBag() - i.data['key'] = 'è' - i.save() - - i.data['key'] = u'è' - i.save() - - def test_get_default(self): - d = HStoreDict() - self.assertIsNone(d.get('none_key', None)) - self.assertIsNone(d.get('none_key')) - - def test_str(self): - d = DataBag() - self.assertEqual(str(d.data), '{}') - - -class TestSerializedDictionaryField(TestCase): - def setUp(self): - SerializedDataBag.objects.all().delete() - Ref.objects.all().delete() - RefsBag.objects.all().delete() - - def _create_bags(self): - alpha = SerializedDataBag.objects.create(name='alpha', data={'v': 1, 'v2': '3', 'v3': {'a': 1}}) - beta = SerializedDataBag.objects.create(name='beta', data={'v': 2, 'v2': [1, '4', 5, {'f': 6}], 'v3': {'a': 2}}) - return alpha, beta - - def _create_bitfield_bags(self): - # create dictionaries with bits as dictionary keys (i.e. bag5 = {'b0':'1', 'b2':'1'}) - for i in range(10): - SerializedDataBag.objects.create( - name='bag%d' % (i,), - data=dict(('b%d' % (bit,), '1') for bit in range(4) if (1 << bit) & i) - ) - - def test_hstore_dict(self): - alpha, beta = self._create_bags() - self.assertEqual(alpha.data, {'v': 1, 'v2': '3', 'v3': {'a': 1}}) - self.assertEqual(beta.data, {'v': 2, 'v2': [1, '4', 5, {'f': 6}], 'v3': {'a': 2}}) - - def test_number(self): - databag = SerializedDataBag(name='number') - databag.data['num'] = 1 - self.assertEqual(databag.data['num'], 1) - - databag.save() - databag = SerializedDataBag.objects.get(name='number') - self.assertEqual(databag.data['num'], 1) - - databag = SerializedDataBag(name='number', data={'num': 1}) - self.assertEqual(databag.data['num'], 1) - - def test_non_id_pk(self): - databag = SerializedDataBagNoID(slug='123', name='abc') - databag.data['num'] = 1 - databag.save() - SerializedDataBagNoID.objects.get(slug='123') - - def test_create_directly(self): - SerializedDataBag.objects.create(name='abc', data={'num': 1}) - SerializedDataBagNoID.objects.create(slug='123', name='abc', data={'num': 1}) - - def test_full_clean(self): - databag = SerializedDataBag(name='number') - databag.data['num'] = 1 - self.assertEqual(databag.data['num'], 1) - - databag.full_clean() - databag.save() - databag = SerializedDataBag.objects.get(name='number') - self.assertEqual(databag.data['num'], 1) - - def test_list(self): - databag = SerializedDataBag.objects.create(name='list', data={'list': ['a', 'b', 'c']}) - databag = SerializedDataBag.objects.get(name='list') - self.assertEqual(databag.data['list'], ['a', 'b', 'c']) - - def test_dictionary(self): - databag = SerializedDataBag.objects.create(name='dict', data={'dict': {'subkey': 'subvalue'}}) - databag = SerializedDataBag.objects.get(name='dict') - self.assertEqual(databag.data['dict'], {'subkey': 'subvalue'}) - - databag.data['dict'] = {'subkey': True, 'list': ['a', 'b', False]} - databag.save() - self.assertEqual(databag.data['dict'], {'subkey': True, 'list': ['a', 'b', False]}) - - def test_boolean(self): - databag = SerializedDataBag.objects.create(name='boolean', data={'boolean': True}) - databag = SerializedDataBag.objects.get(name='boolean') - self.assertEqual(databag.data['boolean'], True) - - def test_is_pickable(self): - m = DefaultsModel() - m.save() - try: - pickle.dumps(m) - except TypeError as e: - self.fail('pickle of DefaultsModel failed: %s' % e) - - def test_empty_instantiation(self): - bag = SerializedDataBag.objects.create(name='bag') - self.assertTrue(isinstance(bag.data, dict)) - self.assertEqual(bag.data, {}) - - def test_empty_querying(self): - SerializedDataBag.objects.create(name='bag') - self.assertTrue(SerializedDataBag.objects.get(data={})) - self.assertTrue(SerializedDataBag.objects.filter(data={})) - self.assertTrue(SerializedDataBag.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.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_null_values(self): - null_v = SerializedDataBag.objects.create(name="test", data={"v": None}) - nonnull_v = SerializedDataBag.objects.create(name="test", data={"v": "item"}) - - r = SerializedDataBag.objects.filter(data__isnull={"v": True}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], null_v) - - r = SerializedDataBag.objects.filter(data__isnull={"v": False}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], nonnull_v) - - def test_named_querying(self): - alpha, beta = self._create_bags() - self.assertEqual(SerializedDataBag.objects.get(name='alpha'), alpha) - self.assertEqual(SerializedDataBag.objects.filter(name='beta')[0], beta) - - def test_aggregates(self): - self._create_bitfield_bags() - - self.assertEqual(SerializedDataBag.objects.filter(data__contains={'b0': '1'}).aggregate(Count('id'))['id__count'], 5) - self.assertEqual(SerializedDataBag.objects.filter(data__contains={'b1': '1'}).aggregate(Count('id'))['id__count'], 4) - - def test_annotations(self): - self._create_bitfield_bags() - - self.assertEqual(SerializedDataBag.objects.annotate(num_id=Count('id')).filter(num_id=1)[0].num_id, 1) - - def test_nested_filtering(self): - self._create_bitfield_bags() - - # Test cumulative successive filters for both dictionaries and other fields - f = SerializedDataBag.objects.all() - self.assertEqual(10, f.count()) - f = f.filter(data__contains={'b0': '1'}) - self.assertEqual(5, f.count()) - f = f.filter(data__contains={'b1': '1'}) - self.assertEqual(2, f.count()) - f = f.filter(name='bag3') - self.assertEqual(1, f.count()) - - def test_unicode_processing(self): - greets = { - u'de': u'Gr\xfc\xdfe, Welt', - u'en': u'hello, world', - u'es': u'hola, ma\xf1ana', - u'he': u'\u05e9\u05dc\u05d5\u05dd, \u05e2\u05d5\u05dc\u05dd', - u'jp': u'\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c', - u'zh': u'\u4f60\u597d\uff0c\u4e16\u754c', - } - SerializedDataBag.objects.create(name='multilang', data=greets) - self.assertEqual(greets, SerializedDataBag.objects.get(name='multilang').data) - - def test_query_escaping(self): - me = self - - def readwrite(s): - # try create and query with potentially illegal characters in the field and dictionary key/value - o = SerializedDataBag.objects.create(name=s, data={s: s}) - me.assertEqual(o, SerializedDataBag.objects.get(name=s, data={s: s})) - readwrite('\' select') - readwrite('% select') - readwrite('\\\' select') - readwrite('-- select') - readwrite('\n select') - readwrite('\r select') - readwrite('* select') - - def test_replace_full_dictionary(self): - SerializedDataBag.objects.create(name='foo', data={'change': 'old value', 'remove': 'baz'}) - - replacement = {'change': 'new value', 'added': 'new'} - SerializedDataBag.objects.filter(name='foo').update(data=replacement) - self.assertEqual(replacement, SerializedDataBag.objects.get(name='foo').data) - - def test_equivalence_querying(self): - alpha, beta = self._create_bags() - for bag in (alpha, beta): - data = {'v': bag.data['v'], 'v2': bag.data['v2'], 'v3': bag.data['v3']} - self.assertEqual(SerializedDataBag.objects.get(data=data), bag) - r = SerializedDataBag.objects.filter(data=data) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - - def test_key_value_subset_querying(self): - alpha, beta = self._create_bags() - for bag in (alpha, beta): - r = SerializedDataBag.objects.filter(data__contains={'v': bag.data['v']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - r = SerializedDataBag.objects.filter(data__contains={'v': bag.data['v'], 'v2': bag.data['v2']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - - def test_key_value_gt_querying(self): - alpha, beta = self._create_bags() - self.assertGreater(beta.data['v'], alpha.data['v']) - r = SerializedDataBag.objects.filter(data__gt={'v': alpha.data['v']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], beta) - r = SerializedDataBag.objects.filter(data__gte={'v': alpha.data['v']}) - self.assertEqual(len(r), 2) - - def test_key_value_gt_casting_number_query(self): - alpha = SerializedDataBag.objects.create(name='alpha', data={'v': 10}) - SerializedDataBag.objects.create(name='alpha', data={'v': 1}) - - r = SerializedDataBag.objects.filter(data__gt={'v': 2}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_key_value_contains_casting_date_query(self): - date = datetime.date(2014, 9, 28) - alpha = SerializedDataBag.objects.create(name='alpha', data={'v': date.isoformat()}) - - r = SerializedDataBag.objects.filter(data__contains={'v': date}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_multiple_key_value_gt_querying(self): - alpha, beta = self._create_bags() - self.assertGreater(beta.data['v'], alpha.data['v']) - r = SerializedDataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v3': alpha.data['v3']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], beta) - r = SerializedDataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v3': beta.data['v3']}) - self.assertEqual(len(r), 0) - r = SerializedDataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v3': alpha.data['v3']}) - self.assertEqual(len(r), 2) - r = SerializedDataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v3': beta.data['v3']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], beta) - - def test_multiple_key_value_lt_querying(self): - alpha, beta = self._create_bags() - self.assertGreater(beta.data['v'], alpha.data['v']) - r = SerializedDataBag.objects.filter(data__lt={'v': beta.data['v'], 'v3': beta.data['v3']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - r = SerializedDataBag.objects.filter(data__lt={'v': beta.data['v'], 'v3': alpha.data['v3']}) - self.assertEqual(len(r), 0) - r = SerializedDataBag.objects.filter(data__lte={'v': beta.data['v'], 'v3': beta.data['v3']}) - self.assertEqual(len(r), 2) - r = SerializedDataBag.objects.filter(data__lte={'v': beta.data['v'], 'v3': alpha.data['v3']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - - def test_key_value_lt_querying(self): - alpha, beta = self._create_bags() - self.assertLess(alpha.data['v'], beta.data['v']) - r = SerializedDataBag.objects.filter(data__lt={'v': beta.data['v']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], alpha) - r = SerializedDataBag.objects.filter(data__lte={'v': beta.data['v']}) - self.assertEqual(len(r), 2) - - def test_multiple_key_subset_querying(self): - alpha, beta = self._create_bags() - for keys in (['v'], ['v', 'v2']): - self.assertEqual(SerializedDataBag.objects.filter(data__contains=keys).count(), 2) - for keys in (['v', 'nv'], ['n1', 'n2']): - self.assertEqual(SerializedDataBag.objects.filter(data__contains=keys).count(), 0) - - def test_single_key_querying(self): - alpha, beta = self._create_bags() - for key in ('v', 'v2'): - self.assertEqual(SerializedDataBag.objects.filter(data__contains=[key]).count(), 2) - for key in ('n1', 'n2'): - self.assertEqual(SerializedDataBag.objects.filter(data__contains=[key]).count(), 0) - - def test_simple_text_icontains_querying(self): - alpha, beta = self._create_bags() - SerializedDataBag.objects.create(name='gamma', data={'theKey': 'someverySpecialValue', 'v2': '3'}) - - self.assertEqual(SerializedDataBag.objects.filter(data__contains='very').count(), 1) - self.assertEqual(SerializedDataBag.objects.filter(data__contains='very')[0].name, 'gamma') - self.assertEqual(SerializedDataBag.objects.filter(data__icontains='specialvalue').count(), 1) - self.assertEqual(SerializedDataBag.objects.filter(data__icontains='specialvalue')[0].name, 'gamma') - - self.assertEqual(SerializedDataBag.objects.filter(data__contains='the').count(), 1) - self.assertEqual(SerializedDataBag.objects.filter(data__contains='the')[0].name, 'gamma') - self.assertEqual(SerializedDataBag.objects.filter(data__icontains='eke').count(), 1) - self.assertEqual(SerializedDataBag.objects.filter(data__icontains='eke')[0].name, 'gamma') - - def test_containment_lookup(self): - alpha, beta = self._create_bags() - - qs = SerializedDataBag.objects.filter(data__contains=1) - self.assertEqual(len(qs), 2) - self.assertEqual(qs[0], alpha) - self.assertEqual(qs[1], beta) - - qs = SerializedDataBag.objects.filter(data__icontains=2) - self.assertEqual(len(qs), 2) - self.assertEqual(list(qs), [alpha, beta]) - - qs = SerializedDataBag.objects.filter(data__contains='3') - self.assertEqual(len(qs), 2) - self.assertEqual(qs[0], alpha) - self.assertEqual(qs[1], beta) - - qs = SerializedDataBag.objects.filter(data__contains='4') - self.assertEqual(len(qs), 1) - self.assertEqual(qs[0], beta) - - # Multiple filters - qs = SerializedDataBag.objects.filter(data__contains='4').filter(data__contains=5) - self.assertEqual(len(qs), 1) - self.assertEqual(qs[0], beta) - - qs = SerializedDataBag.objects.filter(data__contains=1).filter(data__contains='4') - self.assertEqual(len(qs), 1) - self.assertEqual(qs[0], beta) - - # Contains value in nested dict - qs = SerializedDataBag.objects.filter(data__contains=6) - self.assertEqual(len(qs), 1) - - def test_invalid_comparison_lookup_values(self): - alpha, beta = self._create_bags() - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__lt=[1, 2])[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__lt=99)[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__lte=[1, 2])[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__lte=99)[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__gt=[1, 2])[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__gt=99)[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__gte=[1, 2])[0] - with self.assertRaises(ValueError): - SerializedDataBag.objects.filter(data__gte=99)[0] - - def test_hkeys(self): - alpha, beta = self._create_bags() - self.assertEqual(SerializedDataBag.objects.hkeys(id=alpha.id, attr='data'), ['v', 'v2', 'v3']) - self.assertEqual(SerializedDataBag.objects.hkeys(id=beta.id, attr='data'), ['v', 'v2', 'v3']) - - def test_hpeek(self): - alpha, beta = self._create_bags() - self.assertEqual(SerializedDataBag.objects.hpeek(id=alpha.id, attr='data', key='v'), 1) - self.assertEqual(SerializedDataBag.objects.filter(id=alpha.id).hpeek(attr='data', key='v'), 1) - self.assertEqual(SerializedDataBag.objects.hpeek(id=alpha.id, attr='data', key='invalid'), None) - - def test_hremove(self): - alpha, beta = self._create_bags() - self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, alpha.data) - SerializedDataBag.objects.filter(name='alpha').hremove('data', 'v2') - self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, {'v': 1, 'v3': {'a': 1}}) - - self.assertEqual(SerializedDataBag.objects.get(name='beta').data, beta.data) - SerializedDataBag.objects.filter(name='beta').hremove('data', ['v', 'v2', 'v3']) - self.assertEqual(SerializedDataBag.objects.get(name='beta').data, {}) - - def test_hslice(self): - alpha, beta = self._create_bags() - self.assertEqual(SerializedDataBag.objects.hslice(id=alpha.id, attr='data', keys=['v']), {'v': 1}) - self.assertEqual(SerializedDataBag.objects.filter(id=alpha.id).hslice(attr='data', keys=['v']), {'v': 1}) - self.assertEqual(SerializedDataBag.objects.hslice(id=alpha.id, attr='data', keys=['ggg']), {}) - - def test_hupdate(self): - alpha, beta = self._create_bags() - self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, alpha.data) - SerializedDataBag.objects.filter(name='alpha').hupdate('data', {'v2': '10', 'v3': {'a': '20'}}) - self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, {'v': 1, 'v2': '10', 'v3': {'a': '20'}}) - - def test_default(self): - m = DefaultsModel() - m.save() - - def test_bad_default(self): - m = BadDefaultsModel() - try: - m.save() - except IntegrityError: - if DJANGO_VERSION[:2] >= (1, 6): - pass - # TODO: remove in future versions of django-hstore - else: - transaction.rollback() - else: - self.assertTrue(False) - - def test_hstoredictionaryexception(self): - # ok - HStoreDict({}) - - # json object string allowed - HStoreDict('{}') - - # None is ok, will be converted to empty dict - HStoreDict(None) - HStoreDict() - - # non-json string not allowed - with self.assertRaises(HStoreDictException): - HStoreDict('wrong') - - # list not allowed - with self.assertRaises(HStoreDictException): - HStoreDict(['wrong']) - - # json array string representation not allowed - with self.assertRaises(HStoreDictException): - HStoreDict('["wrong"]') - - # number not allowed - with self.assertRaises(HStoreDictException): - HStoreDict(3) - - def test_hstoredictionary_unicode_vs_str(self): - d = HStoreDict({'test': 'test'}) - self.assertEqual(d.__str__(), d.__unicode__()) - - def test_hstore_model_field_validation(self): - d = SerializedDataBag() - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = 'test' - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = '["test"]' - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = ["test"] - - with self.assertRaises(ValidationError): - d.full_clean() - - d.data = { - 'a': 1, - 'b': 2.2, - 'c': ['a', 'b'], - 'd': {'test': 'test'} - } - - with self.assertRaises(ValidationError): - d.full_clean() - - def test_admin_widget(self): - alpha, beta = self._create_bags() - - # create admin user - admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) - admin.set_password('tester') - admin.save() - # login as admin - self.client.login(username='admin', password='tester') - - # access admin change form page - url = reverse('admin:django_hstore_tests_serializeddatabag_change', args=[alpha.id]) - response = self.client.get(url) - # ensure textarea with id="id_data" is there - self.assertContains(response, 'textarea') - self.assertContains(response, 'id_data') - - def test_dictionary_default_admin_widget(self): - class HForm(forms.ModelForm): - class Meta: - model = SerializedDataBag - exclude = [] - - form = HForm() - self.assertEqual(form.fields['data'].widget.__class__, SerializedDictionaryFieldWidget) - - def test_dictionary_custom_admin_widget(self): - class CustomWidget(forms.Widget): - pass - - class HForm(forms.ModelForm): - class Meta: - model = SerializedDataBag - widgets = {'data': CustomWidget} - exclude = [] - - form = HForm() - self.assertEqual(form.fields['data'].widget.__class__, CustomWidget) - - def test_references_default_admin_widget(self): - class HForm(forms.ModelForm): - class Meta: - model = RefsBag - exclude = [] - - form = HForm() - self.assertEqual(form.fields['refs'].widget.__class__, ReferencesFieldWidget) - - def test_references_custom_admin_widget(self): - class CustomWidget(forms.Widget): - pass - - class HForm(forms.ModelForm): - class Meta: - model = RefsBag - widgets = {'refs': CustomWidget} - exclude = [] - - form = HForm() - self.assertEqual(form.fields['refs'].widget.__class__, CustomWidget) - - def test_get_version(self): - get_version() - - def test_unique_together(self): - d = UniqueTogetherDataBag() - d.name = 'test' - d.data = {'test': 'test '} - d.full_clean() - d.save() - - d = UniqueTogetherDataBag() - d.name = 'test' - d.data = {'test': 'test '} - with self.assertRaises(ValidationError): - d.full_clean() - - def test_properties_hstore(self): - """ - Make sure the hstore field does what it is supposed to. - """ - instance = SerializedDataBag() - test_props = {'foo': 'bar', 'size': '3'} - - instance.name = "foo" - instance.data = test_props - instance.save() - - self.assertIsInstance(instance.data, dict) - self.assertEqual(instance.data, test_props) - instance = SerializedDataBag.objects.get(pk=instance.pk) - - self.assertIsInstance(instance.data, dict) - - self.assertEqual(instance.data, test_props) - self.assertEqual(instance.data['size'], '3') - self.assertIn('foo', instance.data) - - def test_unicode(self): - i = SerializedDataBag() - i.data['key'] = 'è' - i.save() - - i.data['key'] = u'è' - i.save() - - def test_get_default(self): - d = HStoreDict() - self.assertIsNone(d.get('none_key', None)) - self.assertIsNone(d.get('none_key')) - - def test_str(self): - d = SerializedDataBag() - self.assertEqual(str(d.data), '{}') - - -from django.core.management import call_command -if sys.version_info[0] >= 3: - from io import StringIO -else: - from StringIO import StringIO - - -class TestSchemaMode(TestCase): - @classmethod - def tearDownClass(cls): - TestSchemaMode._delete_migrations() - - @classmethod - def _delete_migrations(cls): - # delete migration files - migration_path = '{0}/{1}'.format(os.path.dirname(__file__), 'migrations') - import shutil - try: - shutil.rmtree(migration_path) - except OSError: - pass - - def _login_as_admin(self): - # create admin user - admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) - admin.set_password('tester') - admin.save() - # login as admin - self.client.login(username='admin', password='tester') - - def test_to_python_conversion(self): - d = SchemaDataBag().data - - d['number'] = 2 - self.assertEqual(d['number'], 2) - - d['boolean'] = True - self.assertTrue(d['boolean']) - - d['boolean'] = False - self.assertFalse(d['boolean']) - - d['float'] = 2.5 - self.assertEqual(d['float'], 2.5) - self.assertEqual(d.get('float'), 2.5) - - def test_dict_get(self): - d = SchemaDataBag().data - - d['number'] = 2 - self.assertEqual(d.get('number'), 2) - self.assertEqual(d.get('default_test', 'default'), 'default') - self.assertIsNone(d.get('default_test')) - - def test_virtual_field_default_value(self): - d = SchemaDataBag() - self.assertEqual(d.number, 0) - self.assertEqual(d.float, 1.0) - - # accessing the HStoreDict key raises KeyError if not assigned previously - with self.assertRaises(KeyError): - d.data['number'] - - def test_virtual_field_called_statically(self): - with self.assertRaises(AttributeError): - SchemaDataBag.number - - def test_schemadatabag_assignment(self): - d = SchemaDataBag() - - d.number = 4 - self.assertEqual(d.data['number'], 4) - - d.float = 2.5 - self.assertEqual(d.data['float'], 2.5) - - d.data['number'] = 5 - self.assertEqual(d.number, 5) - - def test_schemadatabag_save(self): - d = SchemaDataBag() - d.name = 'test' - d.number = 4 - d.float = 2.0 - d.save() - - d = SchemaDataBag.objects.get(pk=d.id) - self.assertEqual(d.number, 4) - self.assertEqual(d.data['number'], 4) - - def test_schemadatabag_validation_error(self): - d = SchemaDataBag() - d.name = 'test' - d.number = 'WRONG' - d.float = 2.0 - - with self.assertRaises(ValidationError): - d.full_clean() - - d.number = 9 - d.float = 'WRONG' - with self.assertRaises(ValidationError): - d.full_clean() - - d.float = 2.0 - d.char = 'test' - d.choice = 'choice1' - d.full_clean() - d.save() - - def test_admin_list(self): - self._login_as_admin() - url = reverse('admin:django_hstore_tests_schemadatabag_changelist') - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - def test_admin_add(self): - self._login_as_admin() - url = reverse('admin:django_hstore_tests_schemadatabag_add') - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - response = self.client.post(url, {'name': 'test_add', 'number': 3, 'float': 2.4}) - d = SchemaDataBag.objects.all()[0] - self.assertEqual(d.name, 'test_add') - self.assertEqual(d.number, 3) - self.assertEqual(d.float, 2.4) - - def test_admin_add_utf8(self): - self._login_as_admin() - url = reverse('admin:django_hstore_tests_schemadatabag_add') - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - response = self.client.post(url, {'name': 'test_add', 'number': 3, 'float': 2.4, 'char': 'è'}) - d = SchemaDataBag.objects.all()[0] - self.assertEqual(d.name, 'test_add') - self.assertEqual(d.number, 3) - self.assertEqual(d.float, 2.4) - - def test_admin_change(self): - self._login_as_admin() - d = SchemaDataBag() - d.name = 'test1' - d.number = 1 - d.float = 2.5 - d.save() - url = reverse('admin:django_hstore_tests_schemadatabag_change', args=[d.id]) - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - response = self.client.post(url, {'name': 'test_change', 'number': 6, 'float': 2.6}) - d = SchemaDataBag.objects.get(pk=d.id) - self.assertEqual(d.name, 'test_change') - self.assertEqual(d.number, 6) - self.assertEqual(d.data['number'], 6) - self.assertEqual(d.float, 2.6) - self.assertEqual(d.data['float'], 2.6) - - def test_create_hstore_virtual_field(self): - integer = create_hstore_virtual_field('IntegerField', {'default': 0}, 'data') - self.assertIsInstance(integer, models.IntegerField) - char = create_hstore_virtual_field('CharField', {'default': 'test', 'blank': True, 'max_length': 10}, 'data') - self.assertIsInstance(char, models.CharField) - text = create_hstore_virtual_field('TextField', {'blank': True}, 'data') - self.assertIsInstance(text, models.TextField) - - def test_create_hstore_virtual_field_wrong_class(self): - with self.assertRaises(ValueError): - create_hstore_virtual_field(float, {'blank': True}, 'data') - - def test_create_hstore_virtual_field_wrong_class_string(self): - with self.assertRaises(ValueError): - create_hstore_virtual_field('IdoNotExist', {'blank': True}, 'data') - - def test_create_hstore_virtual_field_concrete_class(self): - integer = create_hstore_virtual_field(models.IntegerField, {'default': 0}, 'data') - url = create_hstore_virtual_field(models.URLField, {'blank': True}, 'data') - self.assertTrue(isinstance(integer, models.IntegerField)) - self.assertTrue(isinstance(url, models.URLField)) - - def test_DictionaryField_with_schema(self): - hstore.DictionaryField(schema=[ - { - 'name': 'number', - 'class': 'IntegerField', - 'kwargs': { - 'default': 0, - 'verbose_name': 'verbose number' - } - } - ]) - - def test_schema_validation_string(self): - with self.assertRaises(ValueError): - hstore.DictionaryField(schema='') - - with self.assertRaises(ValueError): - hstore.DictionaryField(schema='WRONG') - - def test_schema_validation_wrong_list(self): - with self.assertRaises(ValueError): - hstore.DictionaryField(schema=[]) - - with self.assertRaises(ValueError): - hstore.DictionaryField(schema=['i am teasing you']) - - def test_schema_validation_wrong_dict(self): - with self.assertRaises(ValueError): - hstore.DictionaryField(schema=[ - {'wrong': 'wrong'} - ]) - - with self.assertRaises(ValueError): - hstore.DictionaryField(schema=[ - {'name': 'test'} - ]) - - def test_utf8(self): - d = SchemaDataBag() - d.name = 'test' - d.number = 4 - d.float = 2.0 - d.char = 'è' - d.full_clean() - d.save() - - d = SchemaDataBag.objects.get(pk=d.id) - self.assertEqual(d.char, u'è') - - d.char = u'è' - d.full_clean() - d.save() - - d = SchemaDataBag.objects.get(pk=d.id) - self.assertEqual(d.char, u'è') - - def test_create(self): - s1 = SchemaDataBag.objects.create( - name='create1', - number=2, - float=2.2, - boolean=True, - boolean_true=False, - choice='choice2', - choice2='choice1', - char='create2', - text='create3' - ) - s1 = SchemaDataBag.objects.get(pk=s1.pk) - self.assertEqual(s1.name, 'create1') - self.assertEqual(s1.number, 2) - self.assertEqual(s1.float, 2.2) - self.assertEqual(s1.boolean, True) - self.assertEqual(s1.boolean_true, False) - self.assertEqual(s1.choice, 'choice2') - self.assertEqual(s1.choice2, 'choice1') - self.assertEqual(s1.char, 'create2') - self.assertEqual(s1.text, 'create3') - - def test_extra_key_regression(self): - s = SchemaDataBag() - s.data['extrakey'] = 2 - self.assertEqual(s.data['extrakey'], '2') - - def test_virtual_date_drf_support(self): - class FakeModel(object): - date = '2014-08-08' - - virtual_date = SchemaDataBag()._hstore_virtual_fields['date'] - - self.assertEqual(virtual_date.value_to_string(FakeModel()), '2014-08-08') - - def test_basefield_attribute(self): - virtual_field = SchemaDataBag()._hstore_virtual_fields['char'] - self.assertEqual(virtual_field.__basefield__.__name__, 'CharField') - - def test_str(self): - d = SchemaDataBag() - self.assertEqual(str(d.data), '{}') - - if DJANGO_VERSION[:2] >= (1, 8): - def test_reload_schema(self): - # cache some stuff - f = SchemaDataBag._meta.get_field('data') - original_schema = list(f.schema) - local_fields_length = len(SchemaDataBag._meta.local_fields) - hstore_virtual_fields_keys = list(SchemaDataBag._hstore_virtual_fields.keys()) - - # original state - d = SchemaDataBag() - self.assertTrue(d.data.schema_mode) - self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) - self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + local_fields_length) - self.assertEqual(len(SchemaDataBag._meta.local_fields), 3) - self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) - for key in hstore_virtual_fields_keys: - self.assertTrue(hasattr(d, key)) - - # schema erased - f.reload_schema(None) - self.assertIsNone(f.schema) - self.assertFalse(f.schema_mode) - self.assertTrue(f.editable) - self.assertEqual(len(SchemaDataBag._meta.virtual_fields), 0) - self.assertEqual(len(SchemaDataBag._meta.fields), local_fields_length) - self.assertEqual(len(SchemaDataBag._meta.local_fields), local_fields_length) - self.assertFalse(hasattr(SchemaDataBag, '_hstore_virtual_fields')) - d = SchemaDataBag() - self.assertFalse(d.data.schema_mode) - for key in hstore_virtual_fields_keys: - self.assertFalse(hasattr(d, key)) - - # reload original schema - f.reload_schema(original_schema) - d = SchemaDataBag() - self.assertTrue(d.data.schema_mode) - self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) - self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + local_fields_length) - self.assertEqual(len(SchemaDataBag._meta.local_fields), 3) - self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) - for key in hstore_virtual_fields_keys: - self.assertTrue(hasattr(d, key)) - else: - # TODO: will removed when django 1.7 will be deprecated - def test_reload_schema(self): - # cache some stuff - f = SchemaDataBag._meta.get_field('data') - original_schema = list(f.schema) - concrete_fields_length = len(SchemaDataBag._meta.concrete_fields) - hstore_virtual_fields_keys = list(SchemaDataBag._hstore_virtual_fields.keys()) - - # original state - d = SchemaDataBag() - self.assertTrue(d.data.schema_mode) - self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) - self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + concrete_fields_length) - self.assertEqual(len(SchemaDataBag._meta.local_fields), len(original_schema) + concrete_fields_length) - self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) - for key in hstore_virtual_fields_keys: - self.assertTrue(hasattr(d, key)) - - # schema erased - f.reload_schema(None) - self.assertIsNone(f.schema) - self.assertFalse(f.schema_mode) - self.assertTrue(f.editable) - self.assertEqual(len(SchemaDataBag._meta.virtual_fields), 0) - self.assertEqual(len(SchemaDataBag._meta.fields), concrete_fields_length) - self.assertEqual(len(SchemaDataBag._meta.local_fields), concrete_fields_length) - self.assertFalse(hasattr(SchemaDataBag, '_hstore_virtual_fields')) - d = SchemaDataBag() - self.assertFalse(d.data.schema_mode) - for key in hstore_virtual_fields_keys: - self.assertFalse(hasattr(d, key)) - - # reload original schema - f.reload_schema(original_schema) - d = SchemaDataBag() - self.assertTrue(d.data.schema_mode) - self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) - self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + concrete_fields_length) - self.assertEqual(len(SchemaDataBag._meta.local_fields), len(original_schema) + concrete_fields_length) - self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) - for key in hstore_virtual_fields_keys: - self.assertTrue(hasattr(d, key)) - - def test_datetime_is_none(self): - """ issue #82 https://github.com/djangonauts/django-hstore/issues/82 """ - d = SchemaDataBag() - d.name = 'datetime' - self.assertIsNone(d.datetime, None) - d.full_clean() - d.save() - d = SchemaDataBag.objects.get(name='datetime') - self.assertIsNone(d.datetime, None) - - def test_none_handling(self): - """ failing test for https://github.com/djangonauts/django-hstore/pull/92 """ - d = NullSchemaDataBag() - self.assertIsNone(d.data) - d.char = 'testing' - d.number = 2 - self.assertEqual(d.char, 'testing') - self.assertEqual(d.number, 2) - d.char = '' - d.number = 0 - self.assertEqual(d.char, '') - self.assertEqual(d.number, 0) - - if DJANGO_VERSION[:2] >= (1, 7): - def _test_migrations_issue_103(self): - """ failing test for https://github.com/djangonauts/django-hstore/issues/103 """ - # start capturing output - output = StringIO() - sys.stdout = output - call_command('makemigrations', 'django_hstore_tests') - # stop capturing print statements - sys.stdout = sys.__stdout__ - self.assertIn('No changes detected', output.getvalue()) - path = '{0}/{1}'.format(os.path.dirname(__file__), 'migrations') - # add a new migration which replicates the bug in #103 - with open('{0}/{1}'.format(path, '0002_issue_103.py'), 'w') as f: - f.write("""# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from django.db import models, migrations -import datetime -import django_hstore.virtual -class Migration(migrations.Migration): - dependencies = [ - ('django_hstore_tests', '0001_initial'), - ] - operations = [ - migrations.AlterField( - model_name='schemadatabag', - name='datetime', - field=django_hstore.virtual.VirtualField(default=datetime.datetime(2015, 4, 19, 16, 44, 9, 417872)), - preserve_default=True, - ), - ]""") - f.close() - # start capturing output - output = StringIO() - sys.stdout = output - call_command('migrate', 'django_hstore_tests', fake_initial=True) - # stop capturing print statements - sys.stdout = sys.__stdout__ - self.assertIn('Applying django_hstore_tests.0002_issue_103... OK', output.getvalue()) - - def _test_migrations_issue_117(self): - """ failing test for https://github.com/djangonauts/django-hstore/issues/117 """ - # start capturing output - output = StringIO() - sys.stdout = output - call_command('makemigrations', 'django_hstore_tests') - # stop capturing print statements - sys.stdout = sys.__stdout__ - self.assertIn('0001_initial', output.getvalue()) - # start capturing output - output = StringIO() - sys.stdout = output - call_command('makemigrations', 'django_hstore_tests') - # stop capturing print statements - sys.stdout = sys.__stdout__ - self.assertIn('No changes detected', output.getvalue()) - - def test_migrations(self): - self._test_migrations_issue_117() - # changes in django 1.8 make this test obsolete - if DJANGO_VERSION[:2] == (1, 7): - self._test_migrations_issue_103() - TestSchemaMode._delete_migrations() - - -class TestNotTransactional(SimpleTestCase): - if DJANGO_VERSION[:2] >= (1, 8): - def setUp(self): - # avoid error "connection already closed" - connection.connect() - - if DJANGO_VERSION[:2] >= (1, 6): - def test_hstore_registring_in_transaction_block(self): - obj1 = DataBag.objects.create(name='alpha1', data={'v': '1', 'v2': '3'}) - obj2 = DataBag.objects.create(name='alpha2', data={'v': '1', 'v2': '3'}) - # Close any existing connection before doing anything - connection.close() - with transaction.atomic(): - qs = DataBag.objects.filter(name__in=["alpha2", "alpha1"]) - self.assertIsInstance(qs[0].data, HStoreDict) - obj1.delete() - obj2.delete() - connection.close() - else: - def test_hstore_registring_in_transaction_block(self): - obj1 = DataBag.objects.create(name='alpha1', data={'v': '1', 'v2': '3'}) - obj2 = DataBag.objects.create(name='alpha2', data={'v': '1', 'v2': '3'}) - # Close any existing connection before doing anything - connection.close() - with transaction.commit_on_success(): - qs = DataBag.objects.filter(name__in=["alpha2", "alpha1"]) - self.assertIsInstance(qs[0].data, HStoreDict) - obj1.delete() - obj2.delete() - connection.close() - - -class TestReferencesField(TestCase): - def setUp(self): - Ref.objects.all().delete() - RefsBag.objects.all().delete() - - def _create_bags(self): - refs = [Ref.objects.create(name=str(i)) for i in range(4)] - alpha = RefsBag.objects.create(name='alpha', refs={'0': refs[0], '1': refs[1]}) - beta = RefsBag.objects.create(name='beta', refs={'0': refs[2], '1': refs[3]}) - return alpha, beta, refs - - def test_empty_instantiation(self): - bag = RefsBag.objects.create(name='bag') - self.assertTrue(isinstance(bag.refs, dict)) - self.assertEqual(bag.refs, {}) - - def test_unsaved_empty_instantiation(self): - bag = RefsBag(name='bag') - self.assertEqual(bag.refs.get('idontexist', 'default'), 'default') - self.assertTrue(isinstance(bag.refs, dict)) - - def test_unsave_empty_instantiation_of_nullable_ref(self): - bag = NullableRefsBag(name='bag') - self.assertEqual(bag.refs.get('idontexist', 'default'), 'default') - self.assertTrue(isinstance(bag.refs, dict)) - - def test_simple_retrieval(self): - alpha, beta, refs = self._create_bags() - alpha = RefsBag.objects.get(name='alpha') - self.assertEqual(Ref.objects.get(name='0'), alpha.refs['0']) - - def test_simple_retrieval_get(self): - alpha, beta, refs = self._create_bags() - alpha = RefsBag.objects.get(name='alpha') - self.assertEqual(Ref.objects.get(name='0'), alpha.refs.get('0')) - - # try getting a non existent key - self.assertEqual(alpha.refs.get('idontexist', 'default'), 'default') - self.assertEqual(alpha.refs.get('idontexist'), None) - - def test_empty_querying(self): - RefsBag.objects.create(name='bag') - self.assertTrue(RefsBag.objects.get(refs={})) - self.assertTrue(RefsBag.objects.filter(refs={})) - self.assertTrue(RefsBag.objects.filter(refs__contains={})) - - def test_equivalence_querying(self): - alpha, beta, refs = self._create_bags() - for bag in (alpha, beta): - refs = {'0': bag.refs['0'], '1': bag.refs['1']} - self.assertEqual(RefsBag.objects.get(refs=refs), bag) - r = RefsBag.objects.filter(refs=refs) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - - def test_key_value_subset_querying(self): - alpha, beta, refs = self._create_bags() - for bag in (alpha, beta): - r = RefsBag.objects.filter(refs__contains={'0': bag.refs['0']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - r = RefsBag.objects.filter(refs__contains={'0': bag.refs['0'], '1': bag.refs['1']}) - self.assertEqual(len(r), 1) - self.assertEqual(r[0], bag) - - def test_multiple_key_subset_querying(self): - alpha, beta, refs = self._create_bags() - for keys in (['0'], ['0', '1']): - self.assertEqual(RefsBag.objects.filter(refs__contains=keys).count(), 2) - for keys in (['0', 'nv'], ['n1', 'n2']): - self.assertEqual(RefsBag.objects.filter(refs__contains=keys).count(), 0) - - def test_single_key_querying(self): - alpha, beta, refs = self._create_bags() - for key in ('0', '1'): - self.assertEqual(RefsBag.objects.filter(refs__contains=[key]).count(), 2) - for key in ('n1', 'n2'): - self.assertEqual(RefsBag.objects.filter(refs__contains=[key]).count(), 0) - - def test_hkeys(self): - alpha, beta, refs = self._create_bags() - self.assertEqual(RefsBag.objects.hkeys(id=alpha.id, attr='refs'), ['0', '1']) - - def test_hpeek(self): - alpha, beta, refs = self._create_bags() - self.assertEqual(RefsBag.objects.hpeek(id=alpha.id, attr='refs', key='0'), refs[0]) - self.assertEqual(RefsBag.objects.filter(id=alpha.id).hpeek(attr='refs', key='0'), refs[0]) - self.assertEqual(RefsBag.objects.hpeek(id=alpha.id, attr='refs', key='invalid'), None) - - def test_hremove(self): - alpha, beta, refs = self._create_bags() - self.assertEqual(RefsBag.objects.get(name='alpha').refs['0'], alpha.refs['0']) - self.assertEqual(RefsBag.objects.get(name='alpha').refs['1'], alpha.refs['1']) - self.assertIn("0", RefsBag.objects.get(name='alpha').refs) - RefsBag.objects.filter(name='alpha').hremove('refs', '0') - self.assertNotIn("0", RefsBag.objects.get(name='alpha').refs) - self.assertIn("1", RefsBag.objects.get(name='alpha').refs) - - self.assertEqual(RefsBag.objects.get(name='beta').refs['0'], beta.refs['0']) - self.assertEqual(RefsBag.objects.get(name='beta').refs['1'], beta.refs['1']) - RefsBag.objects.filter(name='beta').hremove('refs', ['0', '1']) - self.assertEqual(RefsBag.objects.get(name='beta').refs, {}) - - def test_hslice(self): - alpha, beta, refs = self._create_bags() - self.assertEqual(RefsBag.objects.hslice(id=alpha.id, attr='refs', keys=['0']), {'0': refs[0]}) - self.assertEqual(RefsBag.objects.filter(id=alpha.id).hslice(attr='refs', keys=['0']), {'0': refs[0]}) - self.assertEqual(RefsBag.objects.hslice(id=alpha.id, attr='refs', keys=['invalid']), {}) - - def test_admin_reference_field(self): - alpha, beta, refs = self._create_bags() - - # create admin user - admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) - admin.set_password('tester') - admin.save() - # login as admin - self.client.login(username='admin', password='tester') - - # access admin change form page - url = reverse('admin:django_hstore_tests_refsbag_change', args=[alpha.id]) - response = self.client.get(url) - # ensure textarea with id="id_data" is there - self.assertContains(response, 'textarea') - self.assertContains(response, 'id_refs') - - def test_unserialize_references_edge_cases(self): - alpha, beta, refs = self._create_bags() - - refs = unserialize_references(alpha.refs) - # repeat - refs = unserialize_references(alpha.refs) - self.assertEqual(len(unserialize_references(refs).keys()), 2) - self.assertEqual(unserialize_references(None), {}) - - def test_unserialize_references(self): - alpha, beta, refs = self._create_bags() - ref = dict(alpha=alpha, beta=beta) - serialized_refs = serialize_references(ref) - unserialized_refs = unserialize_references(serialized_refs) - self.assertEqual(unserialized_refs['alpha'].pk, alpha.pk) - self.assertEqual(unserialized_refs['beta'].pk, beta.pk) - - def test_serialize_references_edge_cases(self): - self.assertEqual(serialize_references(None), {}) - self.assertEqual(serialize_references({'test': 'test'}), {'test': 'test'}) - - def test_acquire_references_value_error(self): - with self.assertRaises(ValueError): - acquire_reference(None) - with self.assertRaises(ValueError): - acquire_reference('') - - def test_acquire_references_none(self): - self.assertIsNone(acquire_reference('django_hstore_tests.models.SchemaDataBag:2')) - - def test_get_cast_for_param(self): - self.assertEqual(get_cast_for_param([], 'a'), '') - self.assertEqual(get_cast_for_param({'a': True}, 'a'), '::boolean') - self.assertEqual(get_cast_for_param({'a': datetime.datetime}, 'a'), '::timestamp') - self.assertEqual(get_cast_for_param({'a': datetime.time}, 'a'), '::time') - self.assertEqual(get_cast_for_param({'a': int}, 'a'), '::bigint') - self.assertEqual(get_cast_for_param({'a': float}, 'a'), '::float8') - from decimal import Decimal - self.assertEqual(get_cast_for_param({'a': Decimal}, 'a'), '::numeric') - - def test_native_contains(self): - d = DataBag() - d.name = "A bag of data" - d.data = { - 'd1': '1', - 'd2': '2' - } - d.save() - result = DataBag.objects.filter(name__contains='of data') - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = DataBag.objects.filter(name__contains='OF data') - self.assertEqual(result.count(), 0) - - def test_native_icontains(self): - d = DataBag() - d.name = "A bag of data" - d.data = { - 'd1': '1', - 'd2': '2' - } - d.save() - result = DataBag.objects.filter(name__icontains='A bAg') - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - - def test_native_gt(self): - d = NumberedDataBag() - d.name = "A bag of data" - d.number = 12 - d.save() - result = NumberedDataBag.objects.filter(number__gt=12) - self.assertEqual(result.count(), 0) - result = NumberedDataBag.objects.filter(number__gt=1) - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = NumberedDataBag.objects.filter(number__gt=13) - self.assertEqual(result.count(), 0) - - def test_native_gte(self): - d = NumberedDataBag() - d.name = "A bag of data" - d.number = 12 - d.save() - result = NumberedDataBag.objects.filter(number__gte=12) - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = NumberedDataBag.objects.filter(number__gte=1) - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = NumberedDataBag.objects.filter(number__gte=13) - self.assertEqual(result.count(), 0) - - def test_native_lt(self): - d = NumberedDataBag() - d.name = "A bag of data" - d.number = 12 - d.save() - result = NumberedDataBag.objects.filter(number__lt=20) - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = NumberedDataBag.objects.filter(number__lt=12) - self.assertEqual(result.count(), 0) - result = NumberedDataBag.objects.filter(number__lt=1) - self.assertEqual(result.count(), 0) - - def test_native_lte(self): - d = NumberedDataBag() - d.name = "A bag of data" - d.number = 12 - d.save() - result = NumberedDataBag.objects.filter(number__lte=12) - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = NumberedDataBag.objects.filter(number__lte=13) - self.assertEqual(result.count(), 1) - self.assertEqual(result[0].pk, d.pk) - result = NumberedDataBag.objects.filter(number__lte=1) - self.assertEqual(result.count(), 0) - - def test_str(self): - r = RefsBag() - self.assertEqual(str(r.refs), '{}') - - -if GEODJANGO_INSTALLED: - from django.contrib.gis.geos import GEOSGeometry - - class TestDictionaryFieldPlusGIS(TestCase): - """ Test DictionaryField with gis backend """ - - def setUp(self): - Location.objects.all().delete() - - pnt1 = GEOSGeometry('POINT(65.5758316 57.1345383)') - pnt2 = GEOSGeometry('POINT(65.2316 57.3423233)') - - def _create_locations(self): - loc1 = Location.objects.create(name='Location1', data={'prop1': '1', 'prop2': 'test_value'}, point=self.pnt1) - loc2 = Location.objects.create(name='Location2', data={'prop1': '2', 'prop2': 'test_value'}, point=self.pnt2) - return loc1, loc2 - - def test_location_create(self): - l1, l2 = self._create_locations() - other_loc = Location.objects.get(point__contains=self.pnt1) - self.assertEqual(other_loc.data, {'prop1': '1', 'prop2': 'test_value'}) - - def test_location_hupdate(self): - l1, l2 = self._create_locations() - Location.objects.filter(point__contains=self.pnt1).hupdate('data', {'prop1': '2'}) - loc = Location.objects.exclude(point__contains=self.pnt2)[0] - self.assertEqual(loc.data, {'prop1': '2', 'prop2': 'test_value'}) - loc = Location.objects.get(point__contains=self.pnt2) - self.assertNotEqual(loc.data, {'prop1': '1', 'prop2': 'test_value'}) - - def test_location_contains(self): - l1, l2 = self._create_locations() - self.assertEqual(Location.objects.filter(data__contains={'prop1': '1'}).count(), 1) - self.assertEqual(Location.objects.filter(data__contains={'prop1': '2'}).count(), 1) - - def test_location_geomanager(self): - l1, l2 = self._create_locations() - d1 = Location.objects.filter(point__distance_lte=(self.pnt1, 70000)) - self.assertEqual(d1.count(), 2) - - def test_manager(self): - from django_hstore.managers import HStoreGeoManager - isinstance(Location.objects, HStoreGeoManager) - hasattr(Location.objects, 'hpeek') - hasattr(Location.objects, 'hslice') - hasattr(Location.objects, 'hkeys') - - # noqa - class TestReferencesFieldPlusGIS(TestDictionaryFieldPlusGIS): - """ Test ReferenceField with gis backend """ - - def _create_locations(self): - loc1 = Location.objects.create(name='Location1', data={'prop1': '1', 'prop2': 'test_value'}, point=self.pnt1) - loc2 = Location.objects.create(name='Location2', data={'prop1': '2', 'prop2': 'test_value'}, point=self.pnt2) - return loc1, loc2 - - def test_location_create(self): - l1, l2 = self._create_locations() - loc_1 = Location.objects.get(point__contains=self.pnt1) - self.assertEqual(loc_1.data, {'prop1': '1', 'prop2': 'test_value'}) - loc_2 = Location.objects.get(point__contains=self.pnt2) - self.assertEqual(loc_2.data, {'prop1': '2', 'prop2': 'test_value'}) - - def test_location_hupdate(self): - l1, l2 = self._create_locations() - Location.objects.filter(point__contains=self.pnt1).hupdate('data', {'prop1': '2'}) - loc = Location.objects.exclude(point__contains=self.pnt2)[0] - self.assertEqual(loc.data, {'prop1': '2', 'prop2': 'test_value'}) - loc = Location.objects.get(point__contains=self.pnt2) - self.assertNotEqual(loc.data, {'prop1': '1', 'prop2': 'test_value'}) diff --git a/tests/django_hstore_tests/tests/__init__.py b/tests/django_hstore_tests/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/django_hstore_tests/tests/test_dictionary_field.py b/tests/django_hstore_tests/tests/test_dictionary_field.py new file mode 100644 index 0000000..0e6c679 --- /dev/null +++ b/tests/django_hstore_tests/tests/test_dictionary_field.py @@ -0,0 +1,688 @@ +# -*- coding: utf-8 -*- +import sys +import json +import pickle +from decimal import Decimal +import datetime + +from django import VERSION as DJANGO_VERSION +from django import forms +from django.db import transaction +from django.db.models.aggregates import Count +from django.db.utils import IntegrityError +from django.core.exceptions import ValidationError +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User +from django.utils.encoding import force_text +from django.test import TestCase + +from django_hstore import get_version +from django_hstore.forms import DictionaryFieldWidget +from django_hstore.fields import HStoreDict +from django_hstore.exceptions import HStoreDictException +from django_hstore.utils import get_cast_for_param + +from django_hstore_tests.models import (DataBag, + DefaultsModel, + NullableDataBag, + BadDefaultsModel, + UniqueTogetherDataBag, + NumberedDataBag) + + +class TestDictionaryField(TestCase): + def setUp(self): + DataBag.objects.all().delete() + + def _create_bags(self): + alpha = DataBag.objects.create(name='alpha', data={'v': '1', 'v2': '3'}) + beta = DataBag.objects.create(name='beta', data={'v': '2', 'v2': '4'}) + return alpha, beta + + def _create_bitfield_bags(self): + # create dictionaries with bits as dictionary keys (i.e. bag5 = {'b0':'1', 'b2':'1'}) + for i in range(10): + DataBag.objects.create(name='bag%d' % (i,), + data=dict(('b%d' % (bit,), '1') for bit in range(4) if (1 << bit) & i)) + + def test_hstore_dict(self): + alpha, beta = self._create_bags() + self.assertEqual(alpha.data, {'v': '1', 'v2': '3'}) + self.assertEqual(beta.data, {'v': '2', 'v2': '4'}) + + def test_decimal(self): + databag = DataBag(name='decimal') + databag.data['dec'] = Decimal('1.01') + self.assertEqual(databag.data['dec'], force_text(Decimal('1.01'))) + + databag.save() + databag = DataBag.objects.get(name='decimal') + self.assertEqual(databag.data['dec'], force_text(Decimal('1.01'))) + + databag = DataBag(name='decimal', data={'dec': Decimal('1.01')}) + self.assertEqual(databag.data['dec'], force_text(Decimal('1.01'))) + + def test_long(self): + if sys.version < '3': + l = long(100000000000) + databag = DataBag(name='long') + databag.data['long'] = l + self.assertEqual(databag.data['long'], force_text(l)) + + databag.save() + databag = DataBag.objects.get(name='long') + self.assertEqual(databag.data['long'], force_text(l)) + + databag = DataBag(name='long', data={'long': l}) + self.assertEqual(databag.data['long'], force_text(l)) + + def test_number(self): + databag = DataBag(name='number') + databag.data['num'] = 1 + self.assertEqual(databag.data['num'], '1') + + databag.save() + databag = DataBag.objects.get(name='number') + self.assertEqual(databag.data['num'], '1') + + databag = DataBag(name='number', data={'num': 1}) + self.assertEqual(databag.data['num'], '1') + + def test_list(self): + databag = DataBag.objects.create(name='list', data={'list': ['a', 'b', 'c']}) + databag = DataBag.objects.get(name='list') + self.assertEqual(json.loads(databag.data['list']), ['a', 'b', 'c']) + + def test_dictionary(self): + databag = DataBag.objects.create(name='dict', data={'dict': {'subkey': 'subvalue'}}) + databag = DataBag.objects.get(name='dict') + self.assertEqual(json.loads(databag.data['dict']), {'subkey': 'subvalue'}) + + databag.data['dict'] = {'subkey': True, 'list': ['a', 'b', False]} + databag.save() + self.assertEqual(json.loads(databag.data['dict']), {'subkey': True, 'list': ['a', 'b', False]}) + + def test_boolean(self): + databag = DataBag.objects.create(name='boolean', data={'boolean': True}) + databag = DataBag.objects.get(name='boolean') + self.assertEqual(json.loads(databag.data['boolean']), True) + + self.assertTrue(DataBag.objects.get(data__contains={'boolean': True})) + + def test_is_pickable(self): + m = DefaultsModel() + m.save() + try: + pickle.dumps(m) + except TypeError as e: + self.fail('pickle of DefaultsModel failed: %s' % e) + + def test_empty_instantiation(self): + bag = DataBag.objects.create(name='bag') + self.assertTrue(isinstance(bag.data, dict)) + self.assertEqual(bag.data, {}) + + def test_empty_querying(self): + DataBag.objects.create(name='bag') + self.assertTrue(DataBag.objects.get(data={})) + self.assertTrue(DataBag.objects.filter(data={})) + 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.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_null_values(self): + null_v = DataBag.objects.create(name="test", data={"v": None}) + nonnull_v = DataBag.objects.create(name="test", data={"v": "item"}) + + r = DataBag.objects.filter(data__isnull={"v": True}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], null_v) + + r = DataBag.objects.filter(data__isnull={"v": False}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], nonnull_v) + + def test_named_querying(self): + alpha, beta = self._create_bags() + self.assertEqual(DataBag.objects.get(name='alpha'), alpha) + self.assertEqual(DataBag.objects.filter(name='beta')[0], beta) + + def test_aggregates(self): + self._create_bitfield_bags() + + self.assertEqual(DataBag.objects.filter(data__contains={'b0': '1'}).aggregate(Count('id'))['id__count'], 5) + self.assertEqual(DataBag.objects.filter(data__contains={'b1': '1'}).aggregate(Count('id'))['id__count'], 4) + + def test_annotations(self): + self._create_bitfield_bags() + + self.assertEqual(DataBag.objects.annotate(num_id=Count('id')).filter(num_id=1)[0].num_id, 1) + + def test_nested_filtering(self): + self._create_bitfield_bags() + + # Test cumulative successive filters for both dictionaries and other fields + f = DataBag.objects.all() + self.assertEqual(10, f.count()) + f = f.filter(data__contains={'b0': '1'}) + self.assertEqual(5, f.count()) + f = f.filter(data__contains={'b1': '1'}) + self.assertEqual(2, f.count()) + f = f.filter(name='bag3') + self.assertEqual(1, f.count()) + + def test_unicode_processing(self): + greets = { + u'de': u'Gr\xfc\xdfe, Welt', + u'en': u'hello, world', + u'es': u'hola, ma\xf1ana', + u'he': u'\u05e9\u05dc\u05d5\u05dd, \u05e2\u05d5\u05dc\u05dd', + u'jp': u'\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c', + u'zh': u'\u4f60\u597d\uff0c\u4e16\u754c', + } + DataBag.objects.create(name='multilang', data=greets) + self.assertEqual(greets, DataBag.objects.get(name='multilang').data) + + def test_query_escaping(self): + me = self + + def readwrite(s): + # try create and query with potentially illegal characters in the field and dictionary key/value + o = DataBag.objects.create(name=s, data={s: s}) + me.assertEqual(o, DataBag.objects.get(name=s, data={s: s})) + readwrite('\' select') + readwrite('% select') + readwrite('\\\' select') + readwrite('-- select') + readwrite('\n select') + readwrite('\r select') + readwrite('* select') + + def test_replace_full_dictionary(self): + DataBag.objects.create(name='foo', data={'change': 'old value', 'remove': 'baz'}) + + replacement = {'change': 'new value', 'added': 'new'} + DataBag.objects.filter(name='foo').update(data=replacement) + self.assertEqual(replacement, DataBag.objects.get(name='foo').data) + + def test_equivalence_querying(self): + alpha, beta = self._create_bags() + for bag in (alpha, beta): + data = {'v': bag.data['v'], 'v2': bag.data['v2']} + self.assertEqual(DataBag.objects.get(data=data), bag) + r = DataBag.objects.filter(data=data) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + + def test_key_value_subset_querying(self): + alpha, beta = self._create_bags() + for bag in (alpha, beta): + r = DataBag.objects.filter(data__contains={'v': bag.data['v']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + r = DataBag.objects.filter(data__contains={'v': bag.data['v'], 'v2': bag.data['v2']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + + def test_value_in_subset_querying(self): + alpha, beta = self._create_bags() + res = DataBag.objects.filter(data__contains={'v': [alpha.data['v']]}) + self.assertEqual(len(res), 1) + self.assertEqual(res[0], alpha) + res = DataBag.objects.filter(data__contains={'v': [alpha.data['v'], beta.data['v']]}) + self.assertEqual(len(res), 2) + self.assertEqual(set(res), set([alpha, beta])) + + # int values are ok + r = DataBag.objects.filter(data__contains={'v': [int(alpha.data['v'])]}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_key_value_gt_querying(self): + alpha, beta = self._create_bags() + self.assertGreater(beta.data['v'], alpha.data['v']) + r = DataBag.objects.filter(data__gt={'v': alpha.data['v']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], beta) + r = DataBag.objects.filter(data__gte={'v': alpha.data['v']}) + self.assertEqual(len(r), 2) + + def test_key_value_gt_casting_number_query(self): + alpha = DataBag.objects.create(name='alpha', data={'v': 10}) + DataBag.objects.create(name='alpha', data={'v': 1}) + + r = DataBag.objects.filter(data__gt={'v': 2}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_key_value_contains_casting_date_query(self): + date = datetime.date(2014, 9, 28) + alpha = DataBag.objects.create(name='alpha', data={'v': date.isoformat()}) + + r = DataBag.objects.filter(data__contains={'v': date}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_multiple_key_value_gt_querying(self): + alpha, beta = self._create_bags() + self.assertGreater(beta.data['v'], alpha.data['v']) + r = DataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v2': alpha.data['v2']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], beta) + r = DataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v2': beta.data['v2']}) + self.assertEqual(len(r), 0) + r = DataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v2': alpha.data['v2']}) + self.assertEqual(len(r), 2) + r = DataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v2': beta.data['v2']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], beta) + + def test_multiple_key_value_lt_querying(self): + alpha, beta = self._create_bags() + self.assertGreater(beta.data['v'], alpha.data['v']) + r = DataBag.objects.filter(data__lt={'v': beta.data['v'], 'v2': beta.data['v2']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + r = DataBag.objects.filter(data__lt={'v': beta.data['v'], 'v2': alpha.data['v2']}) + self.assertEqual(len(r), 0) + r = DataBag.objects.filter(data__lte={'v': beta.data['v'], 'v2': beta.data['v2']}) + self.assertEqual(len(r), 2) + r = DataBag.objects.filter(data__lte={'v': beta.data['v'], 'v2': alpha.data['v2']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_key_value_lt_querying(self): + alpha, beta = self._create_bags() + self.assertLess(alpha.data['v'], beta.data['v']) + r = DataBag.objects.filter(data__lt={'v': beta.data['v']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + r = DataBag.objects.filter(data__lte={'v': beta.data['v']}) + self.assertEqual(len(r), 2) + + def test_multiple_key_subset_querying(self): + alpha, beta = self._create_bags() + for keys in (['v'], ['v', 'v2']): + self.assertEqual(DataBag.objects.filter(data__contains=keys).count(), 2) + for keys in (['v', 'nv'], ['n1', 'n2']): + self.assertEqual(DataBag.objects.filter(data__contains=keys).count(), 0) + + def test_single_key_querying(self): + alpha, beta = self._create_bags() + for key in ('v', 'v2'): + self.assertEqual(DataBag.objects.filter(data__contains=[key]).count(), 2) + for key in ('n1', 'n2'): + self.assertEqual(DataBag.objects.filter(data__contains=[key]).count(), 0) + + def test_simple_text_icontains_querying(self): + alpha, beta = self._create_bags() + DataBag.objects.create(name='gamma', data={'theKey': 'someverySpecialValue', 'v2': '3'}) + + self.assertEqual(DataBag.objects.filter(data__contains='very').count(), 1) + self.assertEqual(DataBag.objects.filter(data__contains='very')[0].name, 'gamma') + self.assertEqual(DataBag.objects.filter(data__icontains='specialvalue').count(), 1) + self.assertEqual(DataBag.objects.filter(data__icontains='specialvalue')[0].name, 'gamma') + + self.assertEqual(DataBag.objects.filter(data__contains='the').count(), 1) + self.assertEqual(DataBag.objects.filter(data__contains='the')[0].name, 'gamma') + self.assertEqual(DataBag.objects.filter(data__icontains='eke').count(), 1) + self.assertEqual(DataBag.objects.filter(data__icontains='eke')[0].name, 'gamma') + + def test_invalid_containment_lookup_values(self): + alpha, beta = self._create_bags() + with self.assertRaises(ValueError): + DataBag.objects.filter(data__contains=99)[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__icontains=99)[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__icontains=[])[0] + + def test_invalid_comparison_lookup_values(self): + alpha, beta = self._create_bags() + with self.assertRaises(ValueError): + DataBag.objects.filter(data__lt=[1, 2])[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__lt=99)[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__lte=[1, 2])[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__lte=99)[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__gt=[1, 2])[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__gt=99)[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__gte=[1, 2])[0] + with self.assertRaises(ValueError): + DataBag.objects.filter(data__gte=99)[0] + + def test_hkeys(self): + alpha, beta = self._create_bags() + self.assertEqual(DataBag.objects.hkeys(id=alpha.id, attr='data'), ['v', 'v2']) + self.assertEqual(DataBag.objects.hkeys(id=beta.id, attr='data'), ['v', 'v2']) + + def test_hpeek(self): + alpha, beta = self._create_bags() + self.assertEqual(DataBag.objects.hpeek(id=alpha.id, attr='data', key='v'), '1') + self.assertEqual(DataBag.objects.filter(id=alpha.id).hpeek(attr='data', key='v'), '1') + self.assertEqual(DataBag.objects.hpeek(id=alpha.id, attr='data', key='invalid'), None) + + def test_hremove(self): + alpha, beta = self._create_bags() + self.assertEqual(DataBag.objects.get(name='alpha').data, alpha.data) + DataBag.objects.filter(name='alpha').hremove('data', 'v2') + self.assertEqual(DataBag.objects.get(name='alpha').data, {'v': '1'}) + + self.assertEqual(DataBag.objects.get(name='beta').data, beta.data) + DataBag.objects.filter(name='beta').hremove('data', ['v', 'v2']) + self.assertEqual(DataBag.objects.get(name='beta').data, {}) + + def test_hslice(self): + alpha, beta = self._create_bags() + self.assertEqual(DataBag.objects.hslice(id=alpha.id, attr='data', keys=['v']), {'v': '1'}) + self.assertEqual(DataBag.objects.filter(id=alpha.id).hslice(attr='data', keys=['v']), {'v': '1'}) + self.assertEqual(DataBag.objects.hslice(id=alpha.id, attr='data', keys=['ggg']), {}) + + def test_hupdate(self): + alpha, beta = self._create_bags() + self.assertEqual(DataBag.objects.get(name='alpha').data, alpha.data) + DataBag.objects.filter(name='alpha').hupdate('data', {'v2': '10', 'v3': '20'}) + self.assertEqual(DataBag.objects.get(name='alpha').data, {'v': '1', 'v2': '10', 'v3': '20'}) + + def test_hupdate_atomic(self): + """ https://github.com/djangonauts/django-hstore/issues/84 """ + if hasattr(transaction, 'atomic'): + with transaction.atomic(): + self.test_hupdate() + + def test_default(self): + m = DefaultsModel() + m.save() + + def test_bad_default(self): + m = BadDefaultsModel() + try: + m.save() + except IntegrityError: + if DJANGO_VERSION[:2] >= (1, 6): + pass + # TODO: remove in future versions of django-hstore + else: + transaction.rollback() + else: + self.assertTrue(False) + + def test_serialization_deserialization(self): + alpha, beta = self._create_bags() + self.assertEqual(json.loads(str(DataBag.objects.get(name='alpha').data)), json.loads(str(alpha.data))) + self.assertEqual(json.loads(str(DataBag.objects.get(name='beta').data)), json.loads(str(beta.data))) + + def test_hstoredictionaryexception(self): + # ok + HStoreDict({}) + + # json object string allowed + HStoreDict('{}') + + # None is ok, will be converted to empty dict + HStoreDict(None) + HStoreDict() + + # non-json string not allowed + with self.assertRaises(HStoreDictException): + HStoreDict('wrong') + + # list not allowed + with self.assertRaises(HStoreDictException): + HStoreDict(['wrong']) + + # json array string representation not allowed + with self.assertRaises(HStoreDictException): + HStoreDict('["wrong"]') + + # number not allowed + with self.assertRaises(HStoreDictException): + HStoreDict(3) + + def test_hstoredictionary_unicode_vs_str(self): + d = HStoreDict({'test': 'test'}) + self.assertEqual(d.__str__(), d.__unicode__()) + + def test_hstore_model_field_validation(self): + d = DataBag() + + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = 'test' + + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = '["test"]' + + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = ["test"] + + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = { + 'a': 1, + 'b': 2.2, + 'c': ['a', 'b'], + 'd': {'test': 'test'} + } + + with self.assertRaises(ValidationError): + d.full_clean() + + def test_admin_widget(self): + alpha, beta = self._create_bags() + + # create admin user + admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) + admin.set_password('tester') + admin.save() + # login as admin + self.client.login(username='admin', password='tester') + + # access admin change form page + url = reverse('admin:django_hstore_tests_databag_change', args=[alpha.id]) + response = self.client.get(url) + # ensure textarea with id="id_data" is there + self.assertContains(response, 'textarea') + self.assertContains(response, 'id_data') + + def test_dictionary_default_admin_widget(self): + class HForm(forms.ModelForm): + class Meta: + model = DataBag + exclude = [] + + form = HForm() + self.assertEqual(form.fields['data'].widget.__class__, DictionaryFieldWidget) + + def test_dictionary_custom_admin_widget(self): + class CustomWidget(forms.Widget): + pass + + class HForm(forms.ModelForm): + class Meta: + model = DataBag + widgets = {'data': CustomWidget} + exclude = [] + + form = HForm() + self.assertEqual(form.fields['data'].widget.__class__, CustomWidget) + + def test_get_version(self): + get_version() + + def test_unique_together(self): + d = UniqueTogetherDataBag() + d.name = 'test' + d.data = {'test': 'test '} + d.full_clean() + d.save() + + d = UniqueTogetherDataBag() + d.name = 'test' + d.data = {'test': 'test '} + with self.assertRaises(ValidationError): + d.full_clean() + + def test_properties_hstore(self): + """ + Make sure the hstore field does what it is supposed to. + """ + from django_hstore.fields import HStoreDict + + instance = DataBag() + test_props = {'foo': 'bar', 'size': '3'} + + instance.name = 'foo' + instance.data = test_props + instance.save() + + self.assertEqual(type(instance.data), HStoreDict) + self.assertEqual(instance.data, test_props) + instance = DataBag.objects.get(pk=instance.pk) + + self.assertEqual(type(instance.data), HStoreDict) + + self.assertEqual(instance.data, test_props) + self.assertEqual(instance.data['size'], '3') + self.assertIn('foo', instance.data) + + def test_unicode(self): + i = DataBag() + i.data['key'] = 'è' + i.save() + + i.data['key'] = u'è' + i.save() + + def test_get_default(self): + d = HStoreDict() + self.assertIsNone(d.get('none_key', None)) + self.assertIsNone(d.get('none_key')) + + def test_str(self): + d = DataBag() + self.assertEqual(str(d.data), '{}') + + def test_native_contains(self): + d = DataBag() + d.name = "A bag of data" + d.data = { + 'd1': '1', + 'd2': '2' + } + d.save() + result = DataBag.objects.filter(name__contains='of data') + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = DataBag.objects.filter(name__contains='OF data') + self.assertEqual(result.count(), 0) + + def test_native_icontains(self): + d = DataBag() + d.name = "A bag of data" + d.data = { + 'd1': '1', + 'd2': '2' + } + d.save() + result = DataBag.objects.filter(name__icontains='A bAg') + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + + def test_native_gt(self): + d = NumberedDataBag() + d.name = "A bag of data" + d.number = 12 + d.save() + result = NumberedDataBag.objects.filter(number__gt=12) + self.assertEqual(result.count(), 0) + result = NumberedDataBag.objects.filter(number__gt=1) + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = NumberedDataBag.objects.filter(number__gt=13) + self.assertEqual(result.count(), 0) + + def test_native_gte(self): + d = NumberedDataBag() + d.name = "A bag of data" + d.number = 12 + d.save() + result = NumberedDataBag.objects.filter(number__gte=12) + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = NumberedDataBag.objects.filter(number__gte=1) + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = NumberedDataBag.objects.filter(number__gte=13) + self.assertEqual(result.count(), 0) + + def test_native_lt(self): + d = NumberedDataBag() + d.name = "A bag of data" + d.number = 12 + d.save() + result = NumberedDataBag.objects.filter(number__lt=20) + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = NumberedDataBag.objects.filter(number__lt=12) + self.assertEqual(result.count(), 0) + result = NumberedDataBag.objects.filter(number__lt=1) + self.assertEqual(result.count(), 0) + + def test_native_lte(self): + d = NumberedDataBag() + d.name = "A bag of data" + d.number = 12 + d.save() + result = NumberedDataBag.objects.filter(number__lte=12) + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = NumberedDataBag.objects.filter(number__lte=13) + self.assertEqual(result.count(), 1) + self.assertEqual(result[0].pk, d.pk) + result = NumberedDataBag.objects.filter(number__lte=1) + self.assertEqual(result.count(), 0) + + def test_get_cast_for_param(self): + self.assertEqual(get_cast_for_param([], 'a'), '') + self.assertEqual(get_cast_for_param({'a': True}, 'a'), '::boolean') + self.assertEqual(get_cast_for_param({'a': datetime.datetime}, 'a'), '::timestamp') + self.assertEqual(get_cast_for_param({'a': datetime.time}, 'a'), '::time') + self.assertEqual(get_cast_for_param({'a': int}, 'a'), '::bigint') + self.assertEqual(get_cast_for_param({'a': float}, 'a'), '::float8') + from decimal import Decimal + self.assertEqual(get_cast_for_param({'a': Decimal}, 'a'), '::numeric') diff --git a/tests/django_hstore_tests/tests/test_gis_integration.py b/tests/django_hstore_tests/tests/test_gis_integration.py new file mode 100644 index 0000000..3a3d9a1 --- /dev/null +++ b/tests/django_hstore_tests/tests/test_gis_integration.py @@ -0,0 +1,73 @@ +from django_hstore.apps import GEODJANGO_INSTALLED + +if GEODJANGO_INSTALLED: + from django.test import TestCase + from django.contrib.gis.geos import GEOSGeometry + from django_hstore_tests.models import Location + + class TestDictionaryFieldPlusGIS(TestCase): + """ Test DictionaryField with gis backend """ + pnt1 = GEOSGeometry('POINT(65.5758316 57.1345383)') + pnt2 = GEOSGeometry('POINT(65.2316 57.3423233)') + + def setUp(self): + Location.objects.all().delete() + + def _create_locations(self): + loc1 = Location.objects.create(name='Location1', data={'prop1': '1', 'prop2': 'test_value'}, point=self.pnt1) + loc2 = Location.objects.create(name='Location2', data={'prop1': '2', 'prop2': 'test_value'}, point=self.pnt2) + return loc1, loc2 + + def test_location_create(self): + l1, l2 = self._create_locations() + other_loc = Location.objects.get(point__contains=self.pnt1) + self.assertEqual(other_loc.data, {'prop1': '1', 'prop2': 'test_value'}) + + def test_location_hupdate(self): + l1, l2 = self._create_locations() + Location.objects.filter(point__contains=self.pnt1).hupdate('data', {'prop1': '2'}) + loc = Location.objects.exclude(point__contains=self.pnt2)[0] + self.assertEqual(loc.data, {'prop1': '2', 'prop2': 'test_value'}) + loc = Location.objects.get(point__contains=self.pnt2) + self.assertNotEqual(loc.data, {'prop1': '1', 'prop2': 'test_value'}) + + def test_location_contains(self): + l1, l2 = self._create_locations() + self.assertEqual(Location.objects.filter(data__contains={'prop1': '1'}).count(), 1) + self.assertEqual(Location.objects.filter(data__contains={'prop1': '2'}).count(), 1) + + def test_location_geomanager(self): + l1, l2 = self._create_locations() + d1 = Location.objects.filter(point__distance_lte=(self.pnt1, 70000)) + self.assertEqual(d1.count(), 2) + + def test_manager(self): + from django_hstore.managers import HStoreGeoManager + isinstance(Location.objects, HStoreGeoManager) + hasattr(Location.objects, 'hpeek') + hasattr(Location.objects, 'hslice') + hasattr(Location.objects, 'hkeys') + + # noqa + class TestReferencesFieldPlusGIS(TestDictionaryFieldPlusGIS): + """ Test ReferenceField with gis backend """ + + def _create_locations(self): + loc1 = Location.objects.create(name='Location1', data={'prop1': '1', 'prop2': 'test_value'}, point=self.pnt1) + loc2 = Location.objects.create(name='Location2', data={'prop1': '2', 'prop2': 'test_value'}, point=self.pnt2) + return loc1, loc2 + + def test_location_create(self): + l1, l2 = self._create_locations() + loc_1 = Location.objects.get(point__contains=self.pnt1) + self.assertEqual(loc_1.data, {'prop1': '1', 'prop2': 'test_value'}) + loc_2 = Location.objects.get(point__contains=self.pnt2) + self.assertEqual(loc_2.data, {'prop1': '2', 'prop2': 'test_value'}) + + def test_location_hupdate(self): + l1, l2 = self._create_locations() + Location.objects.filter(point__contains=self.pnt1).hupdate('data', {'prop1': '2'}) + loc = Location.objects.exclude(point__contains=self.pnt2)[0] + self.assertEqual(loc.data, {'prop1': '2', 'prop2': 'test_value'}) + loc = Location.objects.get(point__contains=self.pnt2) + self.assertNotEqual(loc.data, {'prop1': '1', 'prop2': 'test_value'}) diff --git a/tests/django_hstore_tests/tests/test_not_transactional.py b/tests/django_hstore_tests/tests/test_not_transactional.py new file mode 100644 index 0000000..272f240 --- /dev/null +++ b/tests/django_hstore_tests/tests/test_not_transactional.py @@ -0,0 +1,39 @@ +from django import VERSION as DJANGO_VERSION +from django.db import transaction, connection +from django.test import SimpleTestCase + +from django_hstore.fields import HStoreDict + +from django_hstore_tests.models import DataBag + + +class TestNotTransactional(SimpleTestCase): + if DJANGO_VERSION[:2] >= (1, 8): + def setUp(self): + # avoid error "connection already closed" + connection.connect() + + if DJANGO_VERSION[:2] >= (1, 6): + def test_hstore_registring_in_transaction_block(self): + obj1 = DataBag.objects.create(name='alpha1', data={'v': '1', 'v2': '3'}) + obj2 = DataBag.objects.create(name='alpha2', data={'v': '1', 'v2': '3'}) + # Close any existing connection before doing anything + connection.close() + with transaction.atomic(): + qs = DataBag.objects.filter(name__in=["alpha2", "alpha1"]) + self.assertIsInstance(qs[0].data, HStoreDict) + obj1.delete() + obj2.delete() + connection.close() + else: + def test_hstore_registring_in_transaction_block(self): + obj1 = DataBag.objects.create(name='alpha1', data={'v': '1', 'v2': '3'}) + obj2 = DataBag.objects.create(name='alpha2', data={'v': '1', 'v2': '3'}) + # Close any existing connection before doing anything + connection.close() + with transaction.commit_on_success(): + qs = DataBag.objects.filter(name__in=["alpha2", "alpha1"]) + self.assertIsInstance(qs[0].data, HStoreDict) + obj1.delete() + obj2.delete() + connection.close() diff --git a/tests/django_hstore_tests/tests/test_reference_field.py b/tests/django_hstore_tests/tests/test_reference_field.py new file mode 100644 index 0000000..f89123c --- /dev/null +++ b/tests/django_hstore_tests/tests/test_reference_field.py @@ -0,0 +1,192 @@ +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User +from django.test import TestCase +from django import forms + +from django_hstore.forms import ReferencesFieldWidget +from django_hstore.utils import unserialize_references, serialize_references, acquire_reference + +from django_hstore_tests.models import Ref, RefsBag, NullableRefsBag + + +class TestReferencesField(TestCase): + def setUp(self): + Ref.objects.all().delete() + RefsBag.objects.all().delete() + + def _create_bags(self): + refs = [Ref.objects.create(name=str(i)) for i in range(4)] + alpha = RefsBag.objects.create(name='alpha', refs={'0': refs[0], '1': refs[1]}) + beta = RefsBag.objects.create(name='beta', refs={'0': refs[2], '1': refs[3]}) + return alpha, beta, refs + + def test_empty_instantiation(self): + bag = RefsBag.objects.create(name='bag') + self.assertTrue(isinstance(bag.refs, dict)) + self.assertEqual(bag.refs, {}) + + def test_unsaved_empty_instantiation(self): + bag = RefsBag(name='bag') + self.assertEqual(bag.refs.get('idontexist', 'default'), 'default') + self.assertTrue(isinstance(bag.refs, dict)) + + def test_unsave_empty_instantiation_of_nullable_ref(self): + bag = NullableRefsBag(name='bag') + self.assertEqual(bag.refs.get('idontexist', 'default'), 'default') + self.assertTrue(isinstance(bag.refs, dict)) + + def test_simple_retrieval(self): + alpha, beta, refs = self._create_bags() + alpha = RefsBag.objects.get(name='alpha') + self.assertEqual(Ref.objects.get(name='0'), alpha.refs['0']) + + def test_simple_retrieval_get(self): + alpha, beta, refs = self._create_bags() + alpha = RefsBag.objects.get(name='alpha') + self.assertEqual(Ref.objects.get(name='0'), alpha.refs.get('0')) + + # try getting a non existent key + self.assertEqual(alpha.refs.get('idontexist', 'default'), 'default') + self.assertEqual(alpha.refs.get('idontexist'), None) + + def test_empty_querying(self): + RefsBag.objects.create(name='bag') + self.assertTrue(RefsBag.objects.get(refs={})) + self.assertTrue(RefsBag.objects.filter(refs={})) + self.assertTrue(RefsBag.objects.filter(refs__contains={})) + + def test_equivalence_querying(self): + alpha, beta, refs = self._create_bags() + for bag in (alpha, beta): + refs = {'0': bag.refs['0'], '1': bag.refs['1']} + self.assertEqual(RefsBag.objects.get(refs=refs), bag) + r = RefsBag.objects.filter(refs=refs) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + + def test_key_value_subset_querying(self): + alpha, beta, refs = self._create_bags() + for bag in (alpha, beta): + r = RefsBag.objects.filter(refs__contains={'0': bag.refs['0']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + r = RefsBag.objects.filter(refs__contains={'0': bag.refs['0'], '1': bag.refs['1']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + + def test_multiple_key_subset_querying(self): + alpha, beta, refs = self._create_bags() + for keys in (['0'], ['0', '1']): + self.assertEqual(RefsBag.objects.filter(refs__contains=keys).count(), 2) + for keys in (['0', 'nv'], ['n1', 'n2']): + self.assertEqual(RefsBag.objects.filter(refs__contains=keys).count(), 0) + + def test_single_key_querying(self): + alpha, beta, refs = self._create_bags() + for key in ('0', '1'): + self.assertEqual(RefsBag.objects.filter(refs__contains=[key]).count(), 2) + for key in ('n1', 'n2'): + self.assertEqual(RefsBag.objects.filter(refs__contains=[key]).count(), 0) + + def test_hkeys(self): + alpha, beta, refs = self._create_bags() + self.assertEqual(RefsBag.objects.hkeys(id=alpha.id, attr='refs'), ['0', '1']) + + def test_hpeek(self): + alpha, beta, refs = self._create_bags() + self.assertEqual(RefsBag.objects.hpeek(id=alpha.id, attr='refs', key='0'), refs[0]) + self.assertEqual(RefsBag.objects.filter(id=alpha.id).hpeek(attr='refs', key='0'), refs[0]) + self.assertEqual(RefsBag.objects.hpeek(id=alpha.id, attr='refs', key='invalid'), None) + + def test_hremove(self): + alpha, beta, refs = self._create_bags() + self.assertEqual(RefsBag.objects.get(name='alpha').refs['0'], alpha.refs['0']) + self.assertEqual(RefsBag.objects.get(name='alpha').refs['1'], alpha.refs['1']) + self.assertIn("0", RefsBag.objects.get(name='alpha').refs) + RefsBag.objects.filter(name='alpha').hremove('refs', '0') + self.assertNotIn("0", RefsBag.objects.get(name='alpha').refs) + self.assertIn("1", RefsBag.objects.get(name='alpha').refs) + + self.assertEqual(RefsBag.objects.get(name='beta').refs['0'], beta.refs['0']) + self.assertEqual(RefsBag.objects.get(name='beta').refs['1'], beta.refs['1']) + RefsBag.objects.filter(name='beta').hremove('refs', ['0', '1']) + self.assertEqual(RefsBag.objects.get(name='beta').refs, {}) + + def test_hslice(self): + alpha, beta, refs = self._create_bags() + self.assertEqual(RefsBag.objects.hslice(id=alpha.id, attr='refs', keys=['0']), {'0': refs[0]}) + self.assertEqual(RefsBag.objects.filter(id=alpha.id).hslice(attr='refs', keys=['0']), {'0': refs[0]}) + self.assertEqual(RefsBag.objects.hslice(id=alpha.id, attr='refs', keys=['invalid']), {}) + + def test_admin_reference_field(self): + alpha, beta, refs = self._create_bags() + + # create admin user + admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) + admin.set_password('tester') + admin.save() + # login as admin + self.client.login(username='admin', password='tester') + + # access admin change form page + url = reverse('admin:django_hstore_tests_refsbag_change', args=[alpha.id]) + response = self.client.get(url) + # ensure textarea with id="id_data" is there + self.assertContains(response, 'textarea') + self.assertContains(response, 'id_refs') + + def test_unserialize_references_edge_cases(self): + alpha, beta, refs = self._create_bags() + + refs = unserialize_references(alpha.refs) + # repeat + refs = unserialize_references(alpha.refs) + self.assertEqual(len(unserialize_references(refs).keys()), 2) + self.assertEqual(unserialize_references(None), {}) + + def test_unserialize_references(self): + alpha, beta, refs = self._create_bags() + ref = dict(alpha=alpha, beta=beta) + serialized_refs = serialize_references(ref) + unserialized_refs = unserialize_references(serialized_refs) + self.assertEqual(unserialized_refs['alpha'].pk, alpha.pk) + self.assertEqual(unserialized_refs['beta'].pk, beta.pk) + + def test_serialize_references_edge_cases(self): + self.assertEqual(serialize_references(None), {}) + self.assertEqual(serialize_references({'test': 'test'}), {'test': 'test'}) + + def test_acquire_references_value_error(self): + with self.assertRaises(ValueError): + acquire_reference(None) + with self.assertRaises(ValueError): + acquire_reference('') + + def test_acquire_references_none(self): + self.assertIsNone(acquire_reference('django_hstore_tests.models.SchemaDataBag:2')) + + def test_str(self): + r = RefsBag() + self.assertEqual(str(r.refs), '{}') + + def test_references_default_admin_widget(self): + class HForm(forms.ModelForm): + class Meta: + model = RefsBag + exclude = [] + + form = HForm() + self.assertEqual(form.fields['refs'].widget.__class__, ReferencesFieldWidget) + + def test_references_custom_admin_widget(self): + class CustomWidget(forms.Widget): + pass + + class HForm(forms.ModelForm): + class Meta: + model = RefsBag + widgets = {'refs': CustomWidget} + exclude = [] + + form = HForm() + self.assertEqual(form.fields['refs'].widget.__class__, CustomWidget) diff --git a/tests/django_hstore_tests/tests/test_schema_mode.py b/tests/django_hstore_tests/tests/test_schema_mode.py new file mode 100644 index 0000000..4d2d383 --- /dev/null +++ b/tests/django_hstore_tests/tests/test_schema_mode.py @@ -0,0 +1,459 @@ +# -*- coding: utf-8 -*- +import os +import sys +import shutil + +if sys.version_info[0] >= 3: + from io import StringIO +else: + from StringIO import StringIO + +from django import VERSION as DJANGO_VERSION +from django.db import models +from django.core.exceptions import ValidationError +from django.core.urlresolvers import reverse +from django.core.management import call_command +from django.contrib.auth.models import User +from django.test import TestCase + +from django_hstore import hstore +from django_hstore.virtual import create_hstore_virtual_field + +from django_hstore_tests.models import SchemaDataBag, NullSchemaDataBag + + +MIGRATION_PATH = '{0}/../{1}'.format(os.path.dirname(__file__), 'migrations') + + +class TestSchemaMode(TestCase): + @classmethod + def tearDownClass(cls): + TestSchemaMode._delete_migrations() + + @classmethod + def _delete_migrations(cls): + # delete migration files + try: + shutil.rmtree(MIGRATION_PATH) + except OSError: + pass + + def _login_as_admin(self): + # create admin user + admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) + admin.set_password('tester') + admin.save() + # login as admin + self.client.login(username='admin', password='tester') + + def test_to_python_conversion(self): + d = SchemaDataBag().data + d['number'] = 2 + self.assertEqual(d['number'], 2) + d['boolean'] = True + self.assertTrue(d['boolean']) + d['boolean'] = False + self.assertFalse(d['boolean']) + d['float'] = 2.5 + self.assertEqual(d['float'], 2.5) + self.assertEqual(d.get('float'), 2.5) + + def test_dict_get(self): + d = SchemaDataBag().data + d['number'] = 2 + self.assertEqual(d.get('number'), 2) + self.assertEqual(d.get('default_test', 'default'), 'default') + self.assertIsNone(d.get('default_test')) + + def test_virtual_field_default_value(self): + d = SchemaDataBag() + self.assertEqual(d.number, 0) + self.assertEqual(d.float, 1.0) + # accessing the HStoreDict key raises KeyError if not assigned previously + with self.assertRaises(KeyError): + d.data['number'] + + def test_virtual_field_called_statically(self): + with self.assertRaises(AttributeError): + SchemaDataBag.number + + def test_schemadatabag_assignment(self): + d = SchemaDataBag() + d.number = 4 + self.assertEqual(d.data['number'], 4) + d.float = 2.5 + self.assertEqual(d.data['float'], 2.5) + d.data['number'] = 5 + self.assertEqual(d.number, 5) + + def test_schemadatabag_save(self): + d = SchemaDataBag() + d.name = 'test' + d.number = 4 + d.float = 2.0 + d.save() + d = SchemaDataBag.objects.get(pk=d.id) + self.assertEqual(d.number, 4) + self.assertEqual(d.data['number'], 4) + + def test_schemadatabag_validation_error(self): + d = SchemaDataBag() + d.name = 'test' + d.number = 'WRONG' + d.float = 2.0 + + with self.assertRaises(ValidationError): + d.full_clean() + + d.number = 9 + d.float = 'WRONG' + with self.assertRaises(ValidationError): + d.full_clean() + + d.float = 2.0 + d.char = 'test' + d.choice = 'choice1' + d.full_clean() + d.save() + + def test_admin_list(self): + self._login_as_admin() + url = reverse('admin:django_hstore_tests_schemadatabag_changelist') + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + def test_admin_add(self): + self._login_as_admin() + url = reverse('admin:django_hstore_tests_schemadatabag_add') + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + response = self.client.post(url, {'name': 'test_add', 'number': 3, 'float': 2.4}) + d = SchemaDataBag.objects.all()[0] + self.assertEqual(d.name, 'test_add') + self.assertEqual(d.number, 3) + self.assertEqual(d.float, 2.4) + + def test_admin_add_utf8(self): + self._login_as_admin() + url = reverse('admin:django_hstore_tests_schemadatabag_add') + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + response = self.client.post(url, {'name': 'test_add', 'number': 3, 'float': 2.4, 'char': 'è'}) + d = SchemaDataBag.objects.all()[0] + self.assertEqual(d.name, 'test_add') + self.assertEqual(d.number, 3) + self.assertEqual(d.float, 2.4) + + def test_admin_change(self): + self._login_as_admin() + d = SchemaDataBag() + d.name = 'test1' + d.number = 1 + d.float = 2.5 + d.save() + url = reverse('admin:django_hstore_tests_schemadatabag_change', args=[d.id]) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + response = self.client.post(url, {'name': 'test_change', 'number': 6, 'float': 2.6}) + d = SchemaDataBag.objects.get(pk=d.id) + self.assertEqual(d.name, 'test_change') + self.assertEqual(d.number, 6) + self.assertEqual(d.data['number'], 6) + self.assertEqual(d.float, 2.6) + self.assertEqual(d.data['float'], 2.6) + + def test_create_hstore_virtual_field(self): + integer = create_hstore_virtual_field('IntegerField', {'default': 0}, 'data') + self.assertIsInstance(integer, models.IntegerField) + char = create_hstore_virtual_field('CharField', {'default': 'test', 'blank': True, 'max_length': 10}, 'data') + self.assertIsInstance(char, models.CharField) + text = create_hstore_virtual_field('TextField', {'blank': True}, 'data') + self.assertIsInstance(text, models.TextField) + + def test_create_hstore_virtual_field_wrong_class(self): + with self.assertRaises(ValueError): + create_hstore_virtual_field(float, {'blank': True}, 'data') + + def test_create_hstore_virtual_field_wrong_class_string(self): + with self.assertRaises(ValueError): + create_hstore_virtual_field('IdoNotExist', {'blank': True}, 'data') + + def test_create_hstore_virtual_field_concrete_class(self): + integer = create_hstore_virtual_field(models.IntegerField, {'default': 0}, 'data') + url = create_hstore_virtual_field(models.URLField, {'blank': True}, 'data') + self.assertTrue(isinstance(integer, models.IntegerField)) + self.assertTrue(isinstance(url, models.URLField)) + + def test_DictionaryField_with_schema(self): + hstore.DictionaryField(schema=[ + { + 'name': 'number', + 'class': 'IntegerField', + 'kwargs': { + 'default': 0, + 'verbose_name': 'verbose number' + } + } + ]) + + def test_schema_validation_string(self): + with self.assertRaises(ValueError): + hstore.DictionaryField(schema='') + + with self.assertRaises(ValueError): + hstore.DictionaryField(schema='WRONG') + + def test_schema_validation_wrong_list(self): + with self.assertRaises(ValueError): + hstore.DictionaryField(schema=[]) + + with self.assertRaises(ValueError): + hstore.DictionaryField(schema=['i am teasing you']) + + def test_schema_validation_wrong_dict(self): + with self.assertRaises(ValueError): + hstore.DictionaryField(schema=[ + {'wrong': 'wrong'} + ]) + + with self.assertRaises(ValueError): + hstore.DictionaryField(schema=[ + {'name': 'test'} + ]) + + def test_utf8(self): + d = SchemaDataBag() + d.name = 'test' + d.number = 4 + d.float = 2.0 + d.char = 'è' + d.full_clean() + d.save() + + d = SchemaDataBag.objects.get(pk=d.id) + self.assertEqual(d.char, u'è') + + d.char = u'è' + d.full_clean() + d.save() + + d = SchemaDataBag.objects.get(pk=d.id) + self.assertEqual(d.char, u'è') + + def test_create(self): + s1 = SchemaDataBag.objects.create( + name='create1', + number=2, + float=2.2, + boolean=True, + boolean_true=False, + choice='choice2', + choice2='choice1', + char='create2', + text='create3' + ) + s1 = SchemaDataBag.objects.get(pk=s1.pk) + self.assertEqual(s1.name, 'create1') + self.assertEqual(s1.number, 2) + self.assertEqual(s1.float, 2.2) + self.assertEqual(s1.boolean, True) + self.assertEqual(s1.boolean_true, False) + self.assertEqual(s1.choice, 'choice2') + self.assertEqual(s1.choice2, 'choice1') + self.assertEqual(s1.char, 'create2') + self.assertEqual(s1.text, 'create3') + + def test_extra_key_regression(self): + s = SchemaDataBag() + s.data['extrakey'] = 2 + self.assertEqual(s.data['extrakey'], '2') + + def test_virtual_date_drf_support(self): + class FakeModel(object): + date = '2014-08-08' + + virtual_date = SchemaDataBag()._hstore_virtual_fields['date'] + + self.assertEqual(virtual_date.value_to_string(FakeModel()), '2014-08-08') + + def test_basefield_attribute(self): + virtual_field = SchemaDataBag()._hstore_virtual_fields['char'] + self.assertEqual(virtual_field.__basefield__.__name__, 'CharField') + + def test_str(self): + d = SchemaDataBag() + self.assertEqual(str(d.data), '{}') + + if DJANGO_VERSION[:2] >= (1, 8): + def test_reload_schema(self): + # cache some stuff + f = SchemaDataBag._meta.get_field('data') + original_schema = list(f.schema) + local_fields_length = len(SchemaDataBag._meta.local_fields) + hstore_virtual_fields_keys = list(SchemaDataBag._hstore_virtual_fields.keys()) + + # original state + d = SchemaDataBag() + self.assertTrue(d.data.schema_mode) + self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) + self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + local_fields_length) + self.assertEqual(len(SchemaDataBag._meta.local_fields), 3) + self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) + for key in hstore_virtual_fields_keys: + self.assertTrue(hasattr(d, key)) + + # schema erased + f.reload_schema(None) + self.assertIsNone(f.schema) + self.assertFalse(f.schema_mode) + self.assertTrue(f.editable) + self.assertEqual(len(SchemaDataBag._meta.virtual_fields), 0) + self.assertEqual(len(SchemaDataBag._meta.fields), local_fields_length) + self.assertEqual(len(SchemaDataBag._meta.local_fields), local_fields_length) + self.assertFalse(hasattr(SchemaDataBag, '_hstore_virtual_fields')) + d = SchemaDataBag() + self.assertFalse(d.data.schema_mode) + for key in hstore_virtual_fields_keys: + self.assertFalse(hasattr(d, key)) + + # reload original schema + f.reload_schema(original_schema) + d = SchemaDataBag() + self.assertTrue(d.data.schema_mode) + self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) + self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + local_fields_length) + self.assertEqual(len(SchemaDataBag._meta.local_fields), 3) + self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) + for key in hstore_virtual_fields_keys: + self.assertTrue(hasattr(d, key)) + else: + # TODO: will removed when django 1.7 will be deprecated + def test_reload_schema(self): + # cache some stuff + f = SchemaDataBag._meta.get_field('data') + original_schema = list(f.schema) + concrete_fields_length = len(SchemaDataBag._meta.concrete_fields) + hstore_virtual_fields_keys = list(SchemaDataBag._hstore_virtual_fields.keys()) + + # original state + d = SchemaDataBag() + self.assertTrue(d.data.schema_mode) + self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) + self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + concrete_fields_length) + self.assertEqual(len(SchemaDataBag._meta.local_fields), len(original_schema) + concrete_fields_length) + self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) + for key in hstore_virtual_fields_keys: + self.assertTrue(hasattr(d, key)) + + # schema erased + f.reload_schema(None) + self.assertIsNone(f.schema) + self.assertFalse(f.schema_mode) + self.assertTrue(f.editable) + self.assertEqual(len(SchemaDataBag._meta.virtual_fields), 0) + self.assertEqual(len(SchemaDataBag._meta.fields), concrete_fields_length) + self.assertEqual(len(SchemaDataBag._meta.local_fields), concrete_fields_length) + self.assertFalse(hasattr(SchemaDataBag, '_hstore_virtual_fields')) + d = SchemaDataBag() + self.assertFalse(d.data.schema_mode) + for key in hstore_virtual_fields_keys: + self.assertFalse(hasattr(d, key)) + + # reload original schema + f.reload_schema(original_schema) + d = SchemaDataBag() + self.assertTrue(d.data.schema_mode) + self.assertEqual(len(SchemaDataBag._meta.virtual_fields), len(original_schema)) + self.assertEqual(len(SchemaDataBag._meta.fields), len(original_schema) + concrete_fields_length) + self.assertEqual(len(SchemaDataBag._meta.local_fields), len(original_schema) + concrete_fields_length) + self.assertTrue(hasattr(SchemaDataBag, '_hstore_virtual_fields')) + for key in hstore_virtual_fields_keys: + self.assertTrue(hasattr(d, key)) + + def test_datetime_is_none(self): + """ issue #82 https://github.com/djangonauts/django-hstore/issues/82 """ + d = SchemaDataBag() + d.name = 'datetime' + self.assertIsNone(d.datetime, None) + d.full_clean() + d.save() + d = SchemaDataBag.objects.get(name='datetime') + self.assertIsNone(d.datetime, None) + + def test_none_handling(self): + """ failing test for https://github.com/djangonauts/django-hstore/pull/92 """ + d = NullSchemaDataBag() + self.assertIsNone(d.data) + d.char = 'testing' + d.number = 2 + self.assertEqual(d.char, 'testing') + self.assertEqual(d.number, 2) + d.char = '' + d.number = 0 + self.assertEqual(d.char, '') + self.assertEqual(d.number, 0) + + if DJANGO_VERSION[:2] >= (1, 7): + def _test_migrations_issue_103(self): + """ failing test for https://github.com/djangonauts/django-hstore/issues/103 """ + # start capturing output + output = StringIO() + sys.stdout = output + call_command('makemigrations', 'django_hstore_tests') + # stop capturing print statements + sys.stdout = sys.__stdout__ + self.assertIn('No changes detected', output.getvalue()) + # add a new migration which replicates the bug in #103 + with open('{0}/{1}'.format(MIGRATION_PATH, '0002_issue_103.py'), 'w') as f: + f.write("""# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from django.db import models, migrations +import datetime +import django_hstore.virtual +class Migration(migrations.Migration): + dependencies = [ + ('django_hstore_tests', '0001_initial'), + ] + operations = [ + migrations.AlterField( + model_name='schemadatabag', + name='datetime', + field=django_hstore.virtual.VirtualField(default=datetime.datetime(2015, 4, 19, 16, 44, 9, 417872)), + preserve_default=True, + ), + ]""") + f.close() + # start capturing output + output = StringIO() + sys.stdout = output + call_command('migrate', 'django_hstore_tests', fake_initial=True) + # stop capturing print statements + sys.stdout = sys.__stdout__ + self.assertIn('Applying django_hstore_tests.0002_issue_103... OK', output.getvalue()) + + def _test_migrations_issue_117(self): + """ failing test for https://github.com/djangonauts/django-hstore/issues/117 """ + # start capturing output + output = StringIO() + sys.stdout = output + call_command('makemigrations', 'django_hstore_tests') + # stop capturing print statements + sys.stdout = sys.__stdout__ + self.assertIn('0001_initial', output.getvalue()) + # start capturing output + output = StringIO() + sys.stdout = output + call_command('makemigrations', 'django_hstore_tests') + # stop capturing print statements + sys.stdout = sys.__stdout__ + self.assertIn('No changes detected', output.getvalue()) + + def test_migrations(self): + self._test_migrations_issue_117() + # changes in django 1.8 make this test obsolete + if DJANGO_VERSION[:2] == (1, 7): + self._test_migrations_issue_103() + TestSchemaMode._delete_migrations() diff --git a/tests/django_hstore_tests/tests/test_serialized_dictionary_field.py b/tests/django_hstore_tests/tests/test_serialized_dictionary_field.py new file mode 100644 index 0000000..f989651 --- /dev/null +++ b/tests/django_hstore_tests/tests/test_serialized_dictionary_field.py @@ -0,0 +1,460 @@ +# -*- coding: utf-8 -*- +import datetime + +from django import forms +from django.db.models.aggregates import Count +from django.core.exceptions import ValidationError +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User +from django.test import TestCase + +from django_hstore.forms import SerializedDictionaryFieldWidget + +from django_hstore_tests.models import SerializedDataBag, SerializedDataBagNoID + + +class TestSerializedDictionaryField(TestCase): + def setUp(self): + SerializedDataBag.objects.all().delete() + + def _create_bags(self): + alpha = SerializedDataBag.objects.create(name='alpha', data={'v': 1, 'v2': '3', 'v3': {'a': 1}}) + beta = SerializedDataBag.objects.create(name='beta', data={'v': 2, 'v2': [1, '4', 5, {'f': 6}], 'v3': {'a': 2}}) + return alpha, beta + + def _create_bitfield_bags(self): + # create dictionaries with bits as dictionary keys (i.e. bag5 = {'b0':'1', 'b2':'1'}) + for i in range(10): + SerializedDataBag.objects.create( + name='bag%d' % (i,), + data=dict(('b%d' % (bit,), '1') for bit in range(4) if (1 << bit) & i) + ) + + def test_hstore_dict(self): + alpha, beta = self._create_bags() + self.assertEqual(alpha.data, {'v': 1, 'v2': '3', 'v3': {'a': 1}}) + self.assertEqual(beta.data, {'v': 2, 'v2': [1, '4', 5, {'f': 6}], 'v3': {'a': 2}}) + + def test_number(self): + databag = SerializedDataBag(name='number') + databag.data['num'] = 1 + self.assertEqual(databag.data['num'], 1) + + databag.save() + databag = SerializedDataBag.objects.get(name='number') + self.assertEqual(databag.data['num'], 1) + + databag = SerializedDataBag(name='number', data={'num': 1}) + self.assertEqual(databag.data['num'], 1) + + def test_non_id_pk(self): + databag = SerializedDataBagNoID(slug='123', name='abc') + databag.data['num'] = 1 + databag.save() + SerializedDataBagNoID.objects.get(slug='123') + + def test_create_directly(self): + SerializedDataBag.objects.create(name='abc', data={'num': 1}) + SerializedDataBagNoID.objects.create(slug='123', name='abc', data={'num': 1}) + + def test_full_clean(self): + databag = SerializedDataBag(name='number') + databag.data['num'] = 1 + self.assertEqual(databag.data['num'], 1) + + databag.full_clean() + databag.save() + databag = SerializedDataBag.objects.get(name='number') + self.assertEqual(databag.data['num'], 1) + + def test_list(self): + databag = SerializedDataBag.objects.create(name='list', data={'list': ['a', 'b', 'c']}) + databag = SerializedDataBag.objects.get(name='list') + self.assertEqual(databag.data['list'], ['a', 'b', 'c']) + + def test_dictionary(self): + databag = SerializedDataBag.objects.create(name='dict', data={'dict': {'subkey': 'subvalue'}}) + databag = SerializedDataBag.objects.get(name='dict') + self.assertEqual(databag.data['dict'], {'subkey': 'subvalue'}) + + databag.data['dict'] = {'subkey': True, 'list': ['a', 'b', False]} + databag.save() + self.assertEqual(databag.data['dict'], {'subkey': True, 'list': ['a', 'b', False]}) + + def test_boolean(self): + databag = SerializedDataBag.objects.create(name='boolean', data={'boolean': True}) + databag = SerializedDataBag.objects.get(name='boolean') + self.assertEqual(databag.data['boolean'], True) + + def test_empty_instantiation(self): + bag = SerializedDataBag.objects.create(name='bag') + self.assertTrue(isinstance(bag.data, dict)) + self.assertEqual(bag.data, {}) + + def test_empty_querying(self): + SerializedDataBag.objects.create(name='bag') + self.assertTrue(SerializedDataBag.objects.get(data={})) + self.assertTrue(SerializedDataBag.objects.filter(data={})) + self.assertTrue(SerializedDataBag.objects.filter(data__contains={})) + + def test_null_values(self): + null_v = SerializedDataBag.objects.create(name="test", data={"v": None}) + nonnull_v = SerializedDataBag.objects.create(name="test", data={"v": "item"}) + + r = SerializedDataBag.objects.filter(data__isnull={"v": True}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], null_v) + + r = SerializedDataBag.objects.filter(data__isnull={"v": False}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], nonnull_v) + + def test_named_querying(self): + alpha, beta = self._create_bags() + self.assertEqual(SerializedDataBag.objects.get(name='alpha'), alpha) + self.assertEqual(SerializedDataBag.objects.filter(name='beta')[0], beta) + + def test_aggregates(self): + self._create_bitfield_bags() + self.assertEqual(SerializedDataBag.objects.filter(data__contains={'b0': '1'}).aggregate(Count('id'))['id__count'], 5) + self.assertEqual(SerializedDataBag.objects.filter(data__contains={'b1': '1'}).aggregate(Count('id'))['id__count'], 4) + + def test_annotations(self): + self._create_bitfield_bags() + self.assertEqual(SerializedDataBag.objects.annotate(num_id=Count('id')).filter(num_id=1)[0].num_id, 1) + + def test_nested_filtering(self): + self._create_bitfield_bags() + # Test cumulative successive filters for both dictionaries and other fields + f = SerializedDataBag.objects.all() + self.assertEqual(10, f.count()) + f = f.filter(data__contains={'b0': '1'}) + self.assertEqual(5, f.count()) + f = f.filter(data__contains={'b1': '1'}) + self.assertEqual(2, f.count()) + f = f.filter(name='bag3') + self.assertEqual(1, f.count()) + + def test_unicode_processing(self): + greets = { + u'de': u'Gr\xfc\xdfe, Welt', + u'en': u'hello, world', + u'es': u'hola, ma\xf1ana', + u'he': u'\u05e9\u05dc\u05d5\u05dd, \u05e2\u05d5\u05dc\u05dd', + u'jp': u'\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c', + u'zh': u'\u4f60\u597d\uff0c\u4e16\u754c', + } + SerializedDataBag.objects.create(name='multilang', data=greets) + self.assertEqual(greets, SerializedDataBag.objects.get(name='multilang').data) + + def test_query_escaping(self): + me = self + + def readwrite(s): + # try create and query with potentially illegal characters in the field and dictionary key/value + o = SerializedDataBag.objects.create(name=s, data={s: s}) + me.assertEqual(o, SerializedDataBag.objects.get(name=s, data={s: s})) + readwrite('\' select') + readwrite('% select') + readwrite('\\\' select') + readwrite('-- select') + readwrite('\n select') + readwrite('\r select') + readwrite('* select') + + def test_replace_full_dictionary(self): + SerializedDataBag.objects.create(name='foo', data={'change': 'old value', 'remove': 'baz'}) + replacement = {'change': 'new value', 'added': 'new'} + SerializedDataBag.objects.filter(name='foo').update(data=replacement) + self.assertEqual(replacement, SerializedDataBag.objects.get(name='foo').data) + + def test_equivalence_querying(self): + alpha, beta = self._create_bags() + for bag in (alpha, beta): + data = {'v': bag.data['v'], 'v2': bag.data['v2'], 'v3': bag.data['v3']} + self.assertEqual(SerializedDataBag.objects.get(data=data), bag) + r = SerializedDataBag.objects.filter(data=data) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + + def test_key_value_subset_querying(self): + alpha, beta = self._create_bags() + for bag in (alpha, beta): + r = SerializedDataBag.objects.filter(data__contains={'v': bag.data['v']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + r = SerializedDataBag.objects.filter(data__contains={'v': bag.data['v'], 'v2': bag.data['v2']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], bag) + + def test_key_value_gt_querying(self): + alpha, beta = self._create_bags() + self.assertGreater(beta.data['v'], alpha.data['v']) + r = SerializedDataBag.objects.filter(data__gt={'v': alpha.data['v']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], beta) + r = SerializedDataBag.objects.filter(data__gte={'v': alpha.data['v']}) + self.assertEqual(len(r), 2) + + def test_key_value_gt_casting_number_query(self): + alpha = SerializedDataBag.objects.create(name='alpha', data={'v': 10}) + SerializedDataBag.objects.create(name='alpha', data={'v': 1}) + + r = SerializedDataBag.objects.filter(data__gt={'v': 2}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_key_value_contains_casting_date_query(self): + date = datetime.date(2014, 9, 28) + alpha = SerializedDataBag.objects.create(name='alpha', data={'v': date.isoformat()}) + + r = SerializedDataBag.objects.filter(data__contains={'v': date}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_multiple_key_value_gt_querying(self): + alpha, beta = self._create_bags() + self.assertGreater(beta.data['v'], alpha.data['v']) + r = SerializedDataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v3': alpha.data['v3']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], beta) + r = SerializedDataBag.objects.filter(data__gt={'v': alpha.data['v'], 'v3': beta.data['v3']}) + self.assertEqual(len(r), 0) + r = SerializedDataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v3': alpha.data['v3']}) + self.assertEqual(len(r), 2) + r = SerializedDataBag.objects.filter(data__gte={'v': alpha.data['v'], 'v3': beta.data['v3']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], beta) + + def test_multiple_key_value_lt_querying(self): + alpha, beta = self._create_bags() + self.assertGreater(beta.data['v'], alpha.data['v']) + r = SerializedDataBag.objects.filter(data__lt={'v': beta.data['v'], 'v3': beta.data['v3']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + r = SerializedDataBag.objects.filter(data__lt={'v': beta.data['v'], 'v3': alpha.data['v3']}) + self.assertEqual(len(r), 0) + r = SerializedDataBag.objects.filter(data__lte={'v': beta.data['v'], 'v3': beta.data['v3']}) + self.assertEqual(len(r), 2) + r = SerializedDataBag.objects.filter(data__lte={'v': beta.data['v'], 'v3': alpha.data['v3']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + + def test_key_value_lt_querying(self): + alpha, beta = self._create_bags() + self.assertLess(alpha.data['v'], beta.data['v']) + r = SerializedDataBag.objects.filter(data__lt={'v': beta.data['v']}) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], alpha) + r = SerializedDataBag.objects.filter(data__lte={'v': beta.data['v']}) + self.assertEqual(len(r), 2) + + def test_multiple_key_subset_querying(self): + alpha, beta = self._create_bags() + for keys in (['v'], ['v', 'v2']): + self.assertEqual(SerializedDataBag.objects.filter(data__contains=keys).count(), 2) + for keys in (['v', 'nv'], ['n1', 'n2']): + self.assertEqual(SerializedDataBag.objects.filter(data__contains=keys).count(), 0) + + def test_single_key_querying(self): + alpha, beta = self._create_bags() + for key in ('v', 'v2'): + self.assertEqual(SerializedDataBag.objects.filter(data__contains=[key]).count(), 2) + for key in ('n1', 'n2'): + self.assertEqual(SerializedDataBag.objects.filter(data__contains=[key]).count(), 0) + + def test_simple_text_icontains_querying(self): + alpha, beta = self._create_bags() + SerializedDataBag.objects.create(name='gamma', data={'theKey': 'someverySpecialValue', 'v2': '3'}) + + self.assertEqual(SerializedDataBag.objects.filter(data__contains='very').count(), 1) + self.assertEqual(SerializedDataBag.objects.filter(data__contains='very')[0].name, 'gamma') + self.assertEqual(SerializedDataBag.objects.filter(data__icontains='specialvalue').count(), 1) + self.assertEqual(SerializedDataBag.objects.filter(data__icontains='specialvalue')[0].name, 'gamma') + + self.assertEqual(SerializedDataBag.objects.filter(data__contains='the').count(), 1) + self.assertEqual(SerializedDataBag.objects.filter(data__contains='the')[0].name, 'gamma') + self.assertEqual(SerializedDataBag.objects.filter(data__icontains='eke').count(), 1) + self.assertEqual(SerializedDataBag.objects.filter(data__icontains='eke')[0].name, 'gamma') + + def test_containment_lookup(self): + alpha, beta = self._create_bags() + + qs = SerializedDataBag.objects.filter(data__contains=1) + self.assertEqual(len(qs), 2) + self.assertEqual(qs[0], alpha) + self.assertEqual(qs[1], beta) + + qs = SerializedDataBag.objects.filter(data__icontains=2) + self.assertEqual(len(qs), 2) + self.assertEqual(list(qs), [alpha, beta]) + + qs = SerializedDataBag.objects.filter(data__contains='3') + self.assertEqual(len(qs), 2) + self.assertEqual(qs[0], alpha) + self.assertEqual(qs[1], beta) + + qs = SerializedDataBag.objects.filter(data__contains='4') + self.assertEqual(len(qs), 1) + self.assertEqual(qs[0], beta) + + # Multiple filters + qs = SerializedDataBag.objects.filter(data__contains='4').filter(data__contains=5) + self.assertEqual(len(qs), 1) + self.assertEqual(qs[0], beta) + + qs = SerializedDataBag.objects.filter(data__contains=1).filter(data__contains='4') + self.assertEqual(len(qs), 1) + self.assertEqual(qs[0], beta) + + # Contains value in nested dict + qs = SerializedDataBag.objects.filter(data__contains=6) + self.assertEqual(len(qs), 1) + + def test_invalid_comparison_lookup_values(self): + alpha, beta = self._create_bags() + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__lt=[1, 2])[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__lt=99)[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__lte=[1, 2])[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__lte=99)[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__gt=[1, 2])[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__gt=99)[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__gte=[1, 2])[0] + with self.assertRaises(ValueError): + SerializedDataBag.objects.filter(data__gte=99)[0] + + def test_hkeys(self): + alpha, beta = self._create_bags() + self.assertEqual(SerializedDataBag.objects.hkeys(id=alpha.id, attr='data'), ['v', 'v2', 'v3']) + self.assertEqual(SerializedDataBag.objects.hkeys(id=beta.id, attr='data'), ['v', 'v2', 'v3']) + + def test_hpeek(self): + alpha, beta = self._create_bags() + self.assertEqual(SerializedDataBag.objects.hpeek(id=alpha.id, attr='data', key='v'), 1) + self.assertEqual(SerializedDataBag.objects.filter(id=alpha.id).hpeek(attr='data', key='v'), 1) + self.assertEqual(SerializedDataBag.objects.hpeek(id=alpha.id, attr='data', key='invalid'), None) + + def test_hremove(self): + alpha, beta = self._create_bags() + self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, alpha.data) + SerializedDataBag.objects.filter(name='alpha').hremove('data', 'v2') + self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, {'v': 1, 'v3': {'a': 1}}) + + self.assertEqual(SerializedDataBag.objects.get(name='beta').data, beta.data) + SerializedDataBag.objects.filter(name='beta').hremove('data', ['v', 'v2', 'v3']) + self.assertEqual(SerializedDataBag.objects.get(name='beta').data, {}) + + def test_hslice(self): + alpha, beta = self._create_bags() + self.assertEqual(SerializedDataBag.objects.hslice(id=alpha.id, attr='data', keys=['v']), {'v': 1}) + self.assertEqual(SerializedDataBag.objects.filter(id=alpha.id).hslice(attr='data', keys=['v']), {'v': 1}) + self.assertEqual(SerializedDataBag.objects.hslice(id=alpha.id, attr='data', keys=['ggg']), {}) + + def test_hupdate(self): + alpha, beta = self._create_bags() + self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, alpha.data) + SerializedDataBag.objects.filter(name='alpha').hupdate('data', {'v2': '10', 'v3': {'a': '20'}}) + self.assertEqual(SerializedDataBag.objects.get(name='alpha').data, {'v': 1, 'v2': '10', 'v3': {'a': '20'}}) + + def test_hstore_model_field_validation(self): + d = SerializedDataBag() + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = 'test' + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = '["test"]' + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = ["test"] + with self.assertRaises(ValidationError): + d.full_clean() + + d.data = { + 'a': 1, + 'b': 2.2, + 'c': ['a', 'b'], + 'd': {'test': 'test'} + } + with self.assertRaises(ValidationError): + d.full_clean() + + def test_admin_widget(self): + alpha, beta = self._create_bags() + # create admin user + admin = User.objects.create(username='admin', password='tester', is_staff=True, is_superuser=True, is_active=True) + admin.set_password('tester') + admin.save() + # login as admin + self.client.login(username='admin', password='tester') + # access admin change form page + url = reverse('admin:django_hstore_tests_serializeddatabag_change', args=[alpha.id]) + response = self.client.get(url) + # ensure textarea with id="id_data" is there + self.assertContains(response, 'textarea') + self.assertContains(response, 'id_data') + + def test_dictionary_default_admin_widget(self): + class HForm(forms.ModelForm): + class Meta: + model = SerializedDataBag + exclude = [] + + form = HForm() + self.assertEqual(form.fields['data'].widget.__class__, SerializedDictionaryFieldWidget) + + def test_dictionary_custom_admin_widget(self): + class CustomWidget(forms.Widget): + pass + + class HForm(forms.ModelForm): + class Meta: + model = SerializedDataBag + widgets = {'data': CustomWidget} + exclude = [] + + form = HForm() + self.assertEqual(form.fields['data'].widget.__class__, CustomWidget) + + def test_properties_hstore(self): + """ + Make sure the hstore field does what it is supposed to. + """ + instance = SerializedDataBag() + test_props = {'foo': 'bar', 'size': '3'} + + instance.name = "foo" + instance.data = test_props + instance.save() + + self.assertIsInstance(instance.data, dict) + self.assertEqual(instance.data, test_props) + instance = SerializedDataBag.objects.get(pk=instance.pk) + + self.assertIsInstance(instance.data, dict) + + self.assertEqual(instance.data, test_props) + self.assertEqual(instance.data['size'], '3') + self.assertIn('foo', instance.data) + + def test_unicode(self): + i = SerializedDataBag() + i.data['key'] = 'è' + i.save() + + i.data['key'] = u'è' + i.save() + + def test_str(self): + d = SerializedDataBag() + self.assertEqual(str(d.data), '{}')