Skip to content

Commit

Permalink
Handles and tests JSONField.
Browse files Browse the repository at this point in the history
  • Loading branch information
BertrandBordage committed Dec 28, 2015
1 parent 275987e commit 3a97912
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 8 deletions.
15 changes: 11 additions & 4 deletions cachalot/tests/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ class Migration(migrations.Migration):
from django.contrib.postgres.fields import (
ArrayField, HStoreField,
IntegerRangeField, FloatRangeField, DateRangeField, DateTimeRangeField)
from django.contrib.postgres.operations import (
HStoreExtension, UnaccentExtension)

Migration.operations.extend((
migrations.AddField('Test', 'duration',
models.DurationField(null=True, blank=True)),
migrations.AddField('Test', 'uuid',
models.UUIDField(null=True, blank=True)),
migrations.RunSQL('CREATE EXTENSION hstore;',
hints={'extension': 'hstore'}),
migrations.RunSQL('CREATE EXTENSION unaccent;',
hints={'extension': 'unaccent'}),
HStoreExtension(),
UnaccentExtension(),
migrations.CreateModel(
name='PostgresModel',
fields=[
Expand All @@ -82,3 +82,10 @@ class Migration(migrations.Migration):
],
),
))

if django_version[:2] >= (1, 9):
from django.contrib.postgres.fields import JSONField
Migration.operations.append(
migrations.AddField('PostgresModel', 'json',
JSONField(null=True, blank=True))
)
6 changes: 6 additions & 0 deletions cachalot/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
FloatField, DecimalField)

DJANGO_GTE_1_8 = django_version[:2] >= (1, 8)
DJANGO_GTE_1_9 = django_version[:2] >= (1, 9)
if DJANGO_GTE_1_8:
from django.db.models import DurationField, UUIDField

Expand Down Expand Up @@ -50,6 +51,8 @@ class TestChild(TestParent):
from django.contrib.postgres.fields import (
ArrayField, HStoreField,
IntegerRangeField, FloatRangeField, DateRangeField, DateTimeRangeField)
if DJANGO_GTE_1_9:
from django.contrib.postgres.fields import JSONField


class PostgresModel(Model):
Expand All @@ -58,6 +61,9 @@ class PostgresModel(Model):

hstore = HStoreField(null=True, blank=True)

if DJANGO_GTE_1_9:
json = JSONField(null=True, blank=True)

int_range = IntegerRangeField(null=True, blank=True)
float_range = FloatRangeField(null=True, blank=True)
date_range = DateRangeField(null=True, blank=True)
Expand Down
106 changes: 104 additions & 2 deletions cachalot/tests/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pytz import timezone

DJANGO_GTE_1_8 = django_version[:2] >= (1, 8)
DJANGO_GTE_1_9 = django_version[:2] >= (1, 9)
if DJANGO_GTE_1_8:
from .models import PostgresModel, Test

Expand All @@ -28,19 +29,41 @@
@override_settings(USE_TZ=True)
class PostgresReadTest(TransactionTestCase):
def setUp(self):
PostgresModel.objects.create(
self.obj1 = PostgresModel(
int_array=[1, 2, 3],
hstore={'a': 'b', 'c': None},
int_range=[1900, 2000], float_range=[-1e3, 9.87654321],
date_range=['1678-03-04', '1741-07-28'],
datetime_range=[datetime(1989, 1, 30, 12, 20,
tzinfo=timezone('Europe/Paris')), None])
PostgresModel.objects.create(
if DJANGO_GTE_1_9:
self.obj1.json = {'a': 1, 'b': 2}
self.obj1.save()

self.obj2 = PostgresModel(
int_array=[4, None, 6],
hstore={'a': '1', 'b': '2'},
int_range=[1989, None], float_range=[0.0, None],
date_range=['1989-01-30', None],
datetime_range=[None, None])
if DJANGO_GTE_1_9:
self.obj2.json = [
'something',
{
'a': 1,
'b': None,
'c': 123.456,
'd': True,
'e': {
'another': 'dict',
'and yet': {
'another': 'one',
'with a list': [],
},
},
},
]
self.obj2.save()

def test_unaccent(self):
Test.objects.create(name='Clémentine')
Expand Down Expand Up @@ -179,6 +202,85 @@ def test_hstore(self):
self.assertListEqual(data16, data15)
self.assertListEqual(data16, [{'a': '1', 'b': '2'}])

@skipUnless(DJANGO_GTE_1_9,
'JSON field is only available in Django >= 1.9')
def test_json(self):
qs = PostgresModel.objects.all()
with self.assertNumQueries(1):
data1 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data2 = [o.json for o in qs.all()]
self.assertListEqual(data2, data1)
self.assertListEqual(data2, [self.obj1.json, self.obj2.json])

# Tests an index.
qs = PostgresModel.objects.filter(json__0='something')
with self.assertNumQueries(1):
data3 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data4 = [o.json for o in qs.all()]
self.assertListEqual(data4, data3)
self.assertListEqual(data4, [self.obj2.json])

qs = PostgresModel.objects.filter(json__0__nonexistent_key='something')
with self.assertNumQueries(1):
data5 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data6 = [o.json for o in qs.all()]
self.assertListEqual(data6, data5)
self.assertListEqual(data6, [])

# Tests a path with spaces.
qs = PostgresModel.objects.filter(
**{'json__1__e__and yet__another': 'one'})
with self.assertNumQueries(1):
data7 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data8 = [o.json for o in qs.all()]
self.assertListEqual(data8, data7)
self.assertListEqual(data8, [self.obj2.json])

qs = PostgresModel.objects.filter(json__contains=['something'])
with self.assertNumQueries(1):
data9 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data10 = [o.json for o in qs.all()]
self.assertListEqual(data10, data9)
self.assertListEqual(data10, [self.obj2.json])

qs = PostgresModel.objects.filter(
json__contained_by={'a': 1, 'b': 2, 'any': 'thing'})
with self.assertNumQueries(1):
data11 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data12 = [o.json for o in qs.all()]
self.assertListEqual(data12, data11)
self.assertListEqual(data12, [self.obj1.json])

qs = PostgresModel.objects.filter(json__has_key='a')
with self.assertNumQueries(1):
data13 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data14 = [o.json for o in qs.all()]
self.assertListEqual(data14, data13)
self.assertListEqual(data14, [self.obj1.json])

qs = PostgresModel.objects.filter(json__has_any_keys=['a', 'b', 'c'])
with self.assertNumQueries(1):
data15 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data16 = [o.json for o in qs.all()]
self.assertListEqual(data16, data15)
self.assertListEqual(data16, [self.obj1.json])

qs = PostgresModel.objects.filter(json__has_keys=['a', 'b'])
with self.assertNumQueries(1):
data17 = [o.json for o in qs.all()]
with self.assertNumQueries(0):
data18 = [o.json for o in qs.all()]
self.assertListEqual(data18, data17)
self.assertListEqual(data18, [self.obj1.json])

def test_int_range(self):
qs = PostgresModel.objects.all()
with self.assertNumQueries(1):
Expand Down
4 changes: 2 additions & 2 deletions cachalot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ class UncachableQuery(Exception):

try:
from psycopg2.extras import (
NumericRange, DateRange, DateTimeRange, DateTimeTZRange, Inet)
NumericRange, DateRange, DateTimeRange, DateTimeTZRange, Inet, Json)
except ImportError:
pass
else:
CACHABLE_PARAM_TYPES.update((
NumericRange, DateRange, DateTimeRange, DateTimeTZRange, Inet))
NumericRange, DateRange, DateTimeRange, DateTimeTZRange, Inet, Json))


def check_parameter_types(params):
Expand Down

0 comments on commit 3a97912

Please sign in to comment.