David Cramer's Blog

Cleaning up with JSON and SQL in Django

Most of our projects lately are now using JSON data storage in the database. This makes it very efficient for storing additional data without having to run alters are gigantic data sets. It also allows us to store different data for different kinds of sets within the same table.

Doing this in Django we originally created a JSONField. A simple custom field which would convert back and from from JSON to a Python object. Mostly this was going from a dictionary to a text-based form in the database (JSON). Recently in one project I took the time to update the field to fix a couple things as well as add support for it in the forms, and thus making it usable within the admin.

So, for the wanting, here's our updated JSONField and the related form field.

from django.db import models
from django import forms
from django.utils import simplejson as json

class JSONWidget(forms.Textarea):
def render(self, name, value, attrs=None):
if not isinstance(value, basestring):
value = json.dumps(value, indent=2)
return super(JSONWidget, self).render(name, value, attrs)

class JSONFormField(forms.CharField):
def __init__(self, *args, **kwargs):
kwargs['widget'] = JSONWidget
super(JSONFormField, self).__init__(*args, **kwargs)

def clean(self, value):
if not value: return
try:
return json.loads(value)
except Exception, exc:
raise forms.ValidationError(u'JSON decode error: %s' % (unicode(exc),))

class JSONField(models.TextField):
__metaclass__ = models.SubfieldBase

def formfield(self, **kwargs):
return super(JSONField, self).formfield(form_class=JSONFormField, **kwargs)

def to_python(self, value):
if isinstance(value, basestring):
value = json.loads(value)
return value

def get_db_prep_save(self, value):
if value is None: return
return json.dumps(value)

<p>def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)