13 февраля 2009 г.

Простой и работающий JSONField для Django

Не успел, я починить PickleField для Django, мне понадобилось создать JSONField. Задача оказалась решенной на раз/два, плюс ко всему добавился маленький и полезный виджет для JSONField'а, который показывает красиво отформатированный JSON в textarea. Сразу скажу, что contribute_to_class метод чуть менне, чем полностью, скопипастен с снипетта 377.

from django.conf import settings
from django.forms.widgets import Textarea
from django.db.models import SubfieldBase, TextField
from django.utils import simplejson


class JSONField(TextField):
    __metaclass__ = SubfieldBase

    def contribute_to_class(self, cls, name):
        super(JSONField, self).contribute_to_class(cls, name)

        def get_json(model):
            return self.get_db_prep_value(getattr(model, self.attname))
        setattr(cls, 'get_%s_json' % self.name, get_json)

        def set_json(model, json):
            setattr(model, self.attname, self.to_python(json))
        setattr(cls, 'set_%s_json' % self.name, set_json)

    def formfield(self, **kwargs):
        kwargs['widget'] = JSONWidget(attrs={'class': 'vLargeTextField'})
        return super(JSONField, self).formfield(**kwargs)

    def get_db_prep_value(self, value):
        return simplejson.dumps(value)

    def to_python(self, value):
        if not isinstance(value, basestring):
            return value

        try:
            return simplejson.loads(value, encoding=settings.DEFAULT_CHARSET)
        except ValueError, e:
            # If string could not parse as JSON it's means that it's Python
            # string saved to JSONField.
            return value

class JSONWidget(Textarea):
    """
    Prettify dumps of all non-string JSON data.
    """
    def render(self, name, value, attrs=None):
        if not isinstance(value, basestring) and value is not None:
            value = simplejson.dumps(value, indent=4, sort_keys=True)
        return super(JSONWidget, self).render(name, value, attrs)

Ну и пару примеров использования, напоследок:

>>> # Пусть у нас есть простая модель
>>> class Sample(models.Model):
...    name = models.CharField(max_length=16)
...    data = JSONField()

>>> # Создадим модель и сохраним в data поле настройки для ShadowBox
>>> sb = Sample.objects.create(name='ShadowBox',
...                            data={'autoDimensions': True,
...                                  'overlayOpacity': 0.5,
...                                  'skipSetup': True})

>>> # Теперь мы хотим напечатать эти настройки где-то в шаблоне
>>> sb.get_data_json()
'{"overlayOpacity": 0.5, "autoDimensions": true, "skipSetup": true}'

>>> # Вспомним, что нам вообщем-то не зачем кастомное значение для overlayOpacity
>>> del sb.data['overlayOpacity']
>>> sb.save()

>>> # И опять напечатаем настройки в шаблоне
>>> sb.get_data_json()
'{"autoDimensions": true, "skipSetup": true}'

зы. ShadowBox - это лучший лайтбокс для любого JavaScript фреймворка, имо.

blog comments powered by Disqus