Created JSON field, descriptor, and formfield to handle json storage on models unifor...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Fri, 8 Oct 2010 18:09:21 +0000 (14:09 -0400)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Fri, 8 Oct 2010 18:12:19 +0000 (14:12 -0400)
models/base.py
models/fields.py
validators.py

index ceeb81e..718429b 100644 (file)
@@ -4,6 +4,7 @@ from django.contrib.contenttypes import generic
 from django.utils import simplejson as json
 from django.core.exceptions import ObjectDoesNotExist
 from philo.exceptions import AncestorDoesNotExist
 from django.utils import simplejson as json
 from django.core.exceptions import ObjectDoesNotExist
 from philo.exceptions import AncestorDoesNotExist
+from philo.models.fields import JSONField
 from philo.utils import ContentTypeRegistryLimiter
 from philo.signals import entity_class_prepared
 from philo.validators import json_validator
 from philo.utils import ContentTypeRegistryLimiter
 from philo.signals import entity_class_prepared
 from philo.validators import json_validator
@@ -37,18 +38,7 @@ class Attribute(models.Model):
        entity_object_id = models.PositiveIntegerField(verbose_name='Entity ID')
        entity = generic.GenericForeignKey('entity_content_type', 'entity_object_id')
        key = models.CharField(max_length=255)
        entity_object_id = models.PositiveIntegerField(verbose_name='Entity ID')
        entity = generic.GenericForeignKey('entity_content_type', 'entity_object_id')
        key = models.CharField(max_length=255)
-       json_value = models.TextField(verbose_name='Value (JSON)', help_text='This value must be valid JSON.', validators=[json_validator])
-       
-       def get_value(self):
-               return json.loads(self.json_value)
-       
-       def set_value(self, value):
-               self.json_value = json.dumps(value)
-       
-       def delete_value(self):
-               self.json_value = json.dumps(None)
-       
-       value = property(get_value, set_value, delete_value)
+       value = JSONField() #verbose_name='Value (JSON)', help_text='This value must be valid JSON.')
        
        def __unicode__(self):
                return u'"%s": %s' % (self.key, self.value)
        
        def __unicode__(self):
                return u'"%s": %s' % (self.key, self.value)
index dbf1886..9483516 100644 (file)
@@ -1,10 +1,10 @@
 from django.db import models
 from django import forms
 from django.db import models
 from django import forms
-from django.core.exceptions import FieldError
+from django.core.exceptions import FieldError, ValidationError
+from django.utils import simplejson as json
 from django.utils.text import capfirst
 from django.utils.text import capfirst
-from philo.models.base import Entity
 from philo.signals import entity_class_prepared
 from philo.signals import entity_class_prepared
-from philo.validators import TemplateValidator
+from philo.validators import TemplateValidator, json_validator
 
 
 __all__ = ('AttributeField', 'RelationshipField')
 
 
 __all__ = ('AttributeField', 'RelationshipField')
@@ -24,6 +24,7 @@ class EntityProxyField(object):
                setattr(sender, self.attname, self.descriptor_class(self))
        
        def contribute_to_class(self, cls, name):
                setattr(sender, self.attname, self.descriptor_class(self))
        
        def contribute_to_class(self, cls, name):
+               from philo.models.base import Entity
                if issubclass(cls, Entity):
                        self.name = name
                        self.attname = name
                if issubclass(cls, Entity):
                        self.name = name
                        self.attname = name
@@ -152,9 +153,58 @@ class TemplateField(models.TextField):
                self.validators.append(TemplateValidator(allow, disallow, secure))
 
 
                self.validators.append(TemplateValidator(allow, disallow, secure))
 
 
+class JSONFormField(forms.Field):
+       def clean(self, value):
+               try:
+                       return json.loads(value)
+               except Exception, e:
+                       raise ValidationError(u'JSON decode error: %s' % e)
+
+
+class JSONDescriptor(object):
+       def __init__(self, field):
+               self.field = field
+       
+       def __get__(self, instance, owner):
+               if instance is None:
+                       raise AttributeError # ?
+               
+               if self.field.name not in instance.__dict__:
+                       json_string = getattr(instance, self.field.attname)
+                       instance.__dict__[self.field.name] = json.loads(json_string)
+               
+               return instance.__dict__[self.field.name]
+       
+       def __set__(self, instance, value):
+               instance.__dict__[self.field.name] = value
+               setattr(instance, self.field.attname, json.dumps(value))
+       
+       def __delete__(self, instance):
+               del(instance.__dict__[self.field.name])
+               setattr(instance, self.field.attname, json.dumps(None))
+
+
+class JSONField(models.TextField):
+       def __init__(self, *args, **kwargs):
+               super(JSONField, self).__init__(*args, **kwargs)
+               self.validators.append(json_validator)
+       
+       def get_attname(self):
+               return "%s_json" % self.name
+       
+       def contribute_to_class(self, cls, name):
+               super(JSONField, self).contribute_to_class(cls, name)
+               setattr(cls, name, JSONDescriptor(self))
+       
+       def formfield(self, *args, **kwargs):
+               kwargs["form_class"] = JSONFormField
+               return super(JSONField, self).formfield(*args, **kwargs)
+
+
 try:
        from south.modelsinspector import add_introspection_rules
 except ImportError:
        pass
 else:
 try:
        from south.modelsinspector import add_introspection_rules
 except ImportError:
        pass
 else:
-       add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])
\ No newline at end of file
+       add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])
+       add_introspection_rules([], ["^philo\.models\.fields\.JSONField"])
\ No newline at end of file
index 1305afb..8b39abd 100644 (file)
@@ -45,8 +45,8 @@ class URLLinkValidator(RegexValidator):
 def json_validator(value):
        try:
                json.loads(value)
 def json_validator(value):
        try:
                json.loads(value)
-       except:
-               raise ValidationError(u'\'%s\' is not valid JSON' % value)
+       except Exception, e:
+               raise ValidationError(u'JSON decode error: %s' % e)
 
 
 class TemplateValidationParser(Parser):
 
 
 class TemplateValidationParser(Parser):