Skip to content

Commit

Permalink
Merge pull request #26 from 18F/master
Browse files Browse the repository at this point in the history
Pass `json_dump_args` to json.dumps
  • Loading branch information
niwinz committed Jun 28, 2015
2 parents ce9042c + 63ceb1b commit 3986101
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 13 deletions.
38 changes: 28 additions & 10 deletions django_pgjson/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@


class JsonAdapter(psycopg2.extras.Json):

def __init__(self, *args, **kwargs):
self.json_dump_args = kwargs.pop('json_dump_args', {})
super(JsonAdapter, self).__init__(*args, **kwargs)

def dumps(self, obj):
return json.dumps(obj, cls=DjangoJSONEncoder)
return json.dumps(obj, cls=DjangoJSONEncoder, **self.json_dump_args)


psycopg2.extensions.register_adapter(dict, JsonAdapter)
Expand All @@ -31,14 +36,19 @@ def dumps(self, obj):
class JsonField(six.with_metaclass(models.SubfieldBase, models.Field)):
empty_strings_allowed = False

def __init__(self, *args, **kwargs):
self.json_dump_args = kwargs.pop('json_dump_args', {})
super(JsonField, self).__init__(*args, **kwargs)

def db_type(self, connection):
if get_version(connection) < 90200:
raise RuntimeError("django_pgjson does not supports postgresql version < 9.2")
return "json"

def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return json.dumps(self.get_prep_value(value), cls=DjangoJSONEncoder)
return json.dumps(self.get_prep_value(value), cls=DjangoJSONEncoder,
**self.json_dump_args)

def get_default(self):
if self.has_default():
Expand All @@ -57,7 +67,7 @@ def to_python(self, value):
return value

def formfield(self, **kwargs):
defaults = {'form_class': JsonFormField}
defaults = {'form_class': jsonFormField(self.json_dump_args)}
defaults.update(kwargs)
return super(JsonField, self).formfield(**defaults)

Expand Down Expand Up @@ -98,7 +108,8 @@ def get_prep_lookup(self, lookup_type, value, prepared=False):
"""
if lookup_type in ['jcontains']:
if not isinstance(value, six.string_types):
value = json.dumps(value, cls=DjangoJSONEncoder)
value = json.dumps(value, cls=DjangoJSONEncoder,
**self.json_dump_args)
if lookup_type in ['jhas_any', 'jhas_all']:
if isinstance(value, six.string_types):
value = [value]
Expand Down Expand Up @@ -130,14 +141,21 @@ def get_prep_lookup(self, lookup_type, value, prepared=False):
JsonBField.register_lookup(JsonBHasAllLookup)


# return a class of JsonFormField

class JsonFormField(forms.CharField):
widget = forms.Textarea
def jsonFormField(json_dump_args_):
class JsonFormField(forms.CharField):

def prepare_value(self, value):
if isinstance(value, six.string_types):
return value
return json.dumps(value, cls=DjangoJSONEncoder)
json_dump_args = json_dump_args_

widget = forms.Textarea

def prepare_value(self, value):
if isinstance(value, six.string_types):
return value
return json.dumps(value, cls=DjangoJSONEncoder,
**self.json_dump_args)
return JsonFormField


# South compatibility
Expand Down
17 changes: 16 additions & 1 deletion doc/doc.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,21 @@ We will implement support for more of the jsonb operators in the near future.
See http://www.postgresql.org/docs/9.4/static/datatype-json.html for more
information on what's possible, and feel free to send a pull request.

Formatting JSON output
^^^^^^^^^^^^^^^^^^^^^^

To control the format of serialized JSON, you may define a `json_dump_args`
dict for a field. Its contents will be passed as arguments to `json.dumps`.

----
data = JsonField(json_dump_args={'indent': 2, 'sort_keys': True})
----

See https://docs.python.org/3/library/json.html#json.dumps
for accepted arguments.

Developers
----------

Expand Down Expand Up @@ -248,4 +263,4 @@ How to contribute
Deprecation policy
~~~~~~~~~~~~~~~~~~

At any momment of time, django-pgjson developers will mantain support for two versions of django.
At any momment of time, django-pgjson developers will mantain support for two versions of django.
22 changes: 22 additions & 0 deletions tests/pg_json_fields/migrations/0003_textmodelwithindent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import django_pgjson.fields


class Migration(migrations.Migration):

dependencies = [
('pg_json_fields', '0002_textmodelwithdefault'),
]

operations = [
migrations.CreateModel(
name='TextModelWithIndent',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)),
('data', django_pgjson.fields.JsonField()),
],
),
]
3 changes: 3 additions & 0 deletions tests/pg_json_fields/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ class TextModelB(models.Model):

class TextModelWithDefault(models.Model):
data = JsonField(blank=True, default={})

class TextModelWithIndent(models.Model):
data = JsonField(json_dump_args={'indent': 2})
9 changes: 9 additions & 0 deletions tests/pg_json_fields/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-

from __future__ import unicode_literals
import json

import django
from django.contrib.admin import AdminSite, ModelAdmin
Expand All @@ -10,6 +11,7 @@
from django_pgjson.fields import JsonField, JsonBField

from .models import TextModel, TextModelB, TextModelWithDefault
from .models import TextModelWithIndent


class JsonFieldTests(TestCase):
Expand Down Expand Up @@ -187,6 +189,13 @@ def test_array_length(self):
qs = self.model_class.objects.filter(data__array_length=3)
self.assertEqual(qs.count(), 1)

def test_indent(self):
obj1 = TextModelWithIndent.objects.create(
data={"name": "foo", "bar": {"baz": 1}})
qs = TextModelWithIndent.objects.filter(data__at_name="foo")
serialized_obj1 = serialize('json', qs)
self.assertIn('\n "name":',
json.loads(serialized_obj1)[0]['fields']['data'])

class JsonBFieldTests(JsonFieldTests):
def setUp(self):
Expand Down
4 changes: 2 additions & 2 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'test',
'USER': '',
'PASSWORD': '',
'USER': 'test',
'PASSWORD': 'test',
'HOST': 'localhost',
'PORT': '',
}
Expand Down

0 comments on commit 3986101

Please sign in to comment.