Created
December 27, 2011 23:59
-
-
Save mariocesar/1525520 to your computer and use it in GitHub Desktop.
Django JSONField, that don't suck!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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