Skip to content

Instantly share code, notes, and snippets.

@mariocesar
Created December 27, 2011 23:59
Show Gist options
  • Save mariocesar/1525520 to your computer and use it in GitHub Desktop.
Save mariocesar/1525520 to your computer and use it in GitHub Desktop.
Django JSONField, that don't suck!
"""
Works with SOUTH, and output on the same widget an HTML list with the value of the field
class MyModel(models.Model):
data = JSONField()
You will need simplejson installed with pip, Django 1.3+
"""
from django.db.models.fields import Field
from django.core.serializers.json import DjangoJSONEncoder
from django.forms.widgets import Textarea
from django.core import exceptions
from django.utils import simplejson as json
from django.utils.safestring import mark_safe
from simplejson.decoder import JSONDecodeError
def dict_to_html(obj, padding=0):
lines = list()
lines.append('<ul style="margin: 0; padding-left: %s0px" >' % padding)
if type(obj) is list:
obj = dict([e for e in enumerate(obj)])
if type(obj) is unicode:
return '%s' % obj
for key, value in obj.iteritems():
if isinstance(value, dict):
lines.append('<li>%s: ' % key)
lines.append('%s' % dict_to_html(value, padding+1))
lines.append('</li>')
else:
lines.append('<li>%s: %s</li>' % (key, value))
lines.append('</ul>')
return '\n'.join(lines)
class RenderJSONWidget(Textarea):
def render(self, name, value, attrs=None):
if value is None or value == '':
json_render = ''
else:
try:
json_render = dict_to_html(json.loads(value))
except JSONDecodeError:
json_render = 'This value is not a valid JSON string.'
json_render = '<div style="display:block; margin-left: 10em;">\n%s' % json_render
textarea_field = super(RenderJSONWidget, self).render(name, value, attrs)
textarea_field += '</div>'
return mark_safe("%s <br/> %s " % (json_render, textarea_field))
class SortedJSONEncoder(DjangoJSONEncoder):
def __init__(self, **kwargs):
kwargs['sort_keys'] = True
super(SortedJSONEncoder, self).__init__(**kwargs)
class JSONField(Field):
description = "JSON encoded string"
default_error_messages = {
'invalid': '%s is not a valid JSON string.',
}
def __init__(self, verbose_name=None, name=None, **kwargs):
Field.__init__(self, verbose_name, name, **kwargs)
def to_python(self, value):
if value is None:
return None
if isinstance(value, dict):
return value
if isinstance(value, list):
return value
try:
return json.loads(value)
except JSONDecodeError:
raise exceptions.ValidationError(self.error_messages['invalid'] % str(value))
def get_prep_value(self, value):
return self.to_python(value)
def get_db_prep_value(self, value, connection, prepared=False):
if not prepared:
value = json.dumps(value, cls=SortedJSONEncoder)
return value
def formfield(self, **kwargs):
kwargs.update({'widget': RenderJSONWidget})
return super(JSONField, self).formfield(**kwargs)
def value_to_string(self, obj):
val = self._get_val_from_obj(obj)
return '' if val is None else json.dumps(val)
def south_field_triple(self):
from south.modelsinspector import introspector
args, kwargs = introspector(self)
return 'django.db.models.TextField', args, kwargs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment