123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- import copy
- from django.db import models
- from django.utils.translation import ugettext_lazy as _
- try:
- from django.utils import six
- except ImportError:
- import six
-
- try:
- import json
- except ImportError:
- from django.utils import simplejson as json
-
- from django.forms import fields
- try:
- from django.forms.utils import ValidationError
- except ImportError:
- from django.forms.util import ValidationError
-
- from .subclassing import SubfieldBase
- from .encoder import JSONEncoder
-
-
- class JSONFormFieldBase(object):
- def __init__(self, *args, **kwargs):
- self.load_kwargs = kwargs.pop('load_kwargs', {})
- super(JSONFormFieldBase, self).__init__(*args, **kwargs)
-
- def to_python(self, value):
- if isinstance(value, six.string_types) and value:
- try:
- return json.loads(value, **self.load_kwargs)
- except ValueError:
- raise ValidationError(_("Enter valid JSON"))
- return value
-
- def clean(self, value):
-
- if not value and not self.required:
- return None
-
- # Trap cleaning errors & bubble them up as JSON errors
- try:
- return super(JSONFormFieldBase, self).clean(value)
- except TypeError:
- raise ValidationError(_("Enter valid JSON"))
-
-
- class JSONFormField(JSONFormFieldBase, fields.CharField):
- pass
-
-
- class JSONCharFormField(JSONFormFieldBase, fields.CharField):
- pass
-
-
- class JSONFieldBase(six.with_metaclass(SubfieldBase, models.Field)):
-
- def __init__(self, *args, **kwargs):
- self.dump_kwargs = kwargs.pop('dump_kwargs', {
- 'cls': JSONEncoder,
- 'separators': (',', ':')
- })
- self.load_kwargs = kwargs.pop('load_kwargs', {})
-
- super(JSONFieldBase, self).__init__(*args, **kwargs)
-
- def pre_init(self, value, obj):
- """Convert a string value to JSON only if it needs to be deserialized.
-
- SubfieldBase metaclass has been modified to call this method instead of
- to_python so that we can check the obj state and determine if it needs to be
- deserialized"""
-
- try:
- if obj._state.adding:
- # Make sure the primary key actually exists on the object before
- # checking if it's empty. This is a special case for South datamigrations
- # see: https://github.com/bradjasper/django-jsonfield/issues/52
- if getattr(obj, "pk", None) is not None:
- if isinstance(value, six.string_types):
- try:
- return json.loads(value, **self.load_kwargs)
- except ValueError:
- raise ValidationError(_("Enter valid JSON"))
-
- except AttributeError:
- # south fake meta class doesn't create proper attributes
- # see this:
- # https://github.com/bradjasper/django-jsonfield/issues/52
- pass
-
- return value
-
- def to_python(self, value):
- """The SubfieldBase metaclass calls pre_init instead of to_python, however to_python
- is still necessary for Django's deserializer"""
- return value
-
- def get_prep_value(self, value):
- """Convert JSON object to a string"""
- if self.null and value is None:
- return None
- return json.dumps(value, **self.dump_kwargs)
-
- def _get_val_from_obj(self, obj):
- # This function created to replace Django deprecated version
- # https://code.djangoproject.com/ticket/24716
- if obj is not None:
- return getattr(obj, self.attname)
- else:
- return self.get_default()
-
- def value_to_string(self, obj):
- value = self._get_val_from_obj(obj)
- return self.get_db_prep_value(value, None)
-
- def value_from_object(self, obj):
- value = super(JSONFieldBase, self).value_from_object(obj)
- if self.null and value is None:
- return None
- return self.dumps_for_display(value)
-
- def dumps_for_display(self, value):
- return json.dumps(value, **self.dump_kwargs)
-
- def formfield(self, **kwargs):
-
- if "form_class" not in kwargs:
- kwargs["form_class"] = self.form_class
-
- field = super(JSONFieldBase, self).formfield(**kwargs)
-
- if isinstance(field, JSONFormFieldBase):
- field.load_kwargs = self.load_kwargs
-
- if not field.help_text:
- field.help_text = "Enter valid JSON"
-
- return field
-
- def get_default(self):
- """
- Returns the default value for this field.
-
- The default implementation on models.Field calls force_unicode
- on the default, which means you can't set arbitrary Python
- objects as the default. To fix this, we just return the value
- without calling force_unicode on it. Note that if you set a
- callable as a default, the field will still call it. It will
- *not* try to pickle and encode it.
-
- """
- if self.has_default():
- if callable(self.default):
- return self.default()
- return copy.deepcopy(self.default)
- # If the field doesn't have a default, then we punt to models.Field.
- return super(JSONFieldBase, self).get_default()
-
-
- class JSONField(JSONFieldBase, models.TextField):
- """JSONField is a generic textfield that serializes/deserializes JSON objects"""
- form_class = JSONFormField
-
- def dumps_for_display(self, value):
- kwargs = {"indent": 2}
- kwargs.update(self.dump_kwargs)
- return json.dumps(value, **kwargs)
-
-
- class JSONCharField(JSONFieldBase, models.CharField):
- """JSONCharField is a generic textfield that serializes/deserializes JSON objects,
- stored in the database like a CharField, which enables it to be used
- e.g. in unique keys"""
- form_class = JSONCharFormField
-
-
- try:
- from south.modelsinspector import add_introspection_rules
- add_introspection_rules([], ["^jsonfield\.fields\.(JSONField|JSONCharField)"])
- except ImportError:
- pass
|