2 from django
.db
import models
3 from django
.utils
.translation
import ugettext_lazy
as _
5 from django
.utils
import six
12 from django
.utils
import simplejson
as json
14 from django
.forms
import fields
16 from django
.forms
.utils
import ValidationError
18 from django
.forms
.util
import ValidationError
20 from .subclassing
import SubfieldBase
21 from .encoder
import JSONEncoder
24 class JSONFormFieldBase(object):
26 def to_python(self
, value
):
27 if isinstance(value
, six
.string_types
):
29 return json
.loads(value
, **self
.load_kwargs
)
31 raise ValidationError(_("Enter valid JSON"))
34 def clean(self
, value
):
36 if not value
and not self
.required
:
39 # Trap cleaning errors & bubble them up as JSON errors
41 return super(JSONFormFieldBase
, self
).clean(value
)
43 raise ValidationError(_("Enter valid JSON"))
46 class JSONFormField(JSONFormFieldBase
, fields
.CharField
):
49 class JSONCharFormField(JSONFormFieldBase
, fields
.CharField
):
53 class JSONFieldBase(six
.with_metaclass(SubfieldBase
, models
.Field
)):
55 def __init__(self
, *args
, **kwargs
):
56 self
.dump_kwargs
= kwargs
.pop('dump_kwargs', {
58 'separators': (',', ':')
60 self
.load_kwargs
= kwargs
.pop('load_kwargs', {})
62 super(JSONFieldBase
, self
).__init__(*args
, **kwargs
)
64 def pre_init(self
, value
, obj
):
65 """Convert a string value to JSON only if it needs to be deserialized.
67 SubfieldBase metaclass has been modified to call this method instead of
68 to_python so that we can check the obj state and determine if it needs to be
73 # Make sure the primary key actually exists on the object before
74 # checking if it's empty. This is a special case for South datamigrations
75 # see: https://github.com/bradjasper/django-jsonfield/issues/52
76 if getattr(obj
, "pk", None) is not None:
77 if isinstance(value
, six
.string_types
):
79 return json
.loads(value
, **self
.load_kwargs
)
81 raise ValidationError(_("Enter valid JSON"))
83 except AttributeError:
84 # south fake meta class doesn't create proper attributes
86 # https://github.com/bradjasper/django-jsonfield/issues/52
91 def to_python(self
, value
):
92 """The SubfieldBase metaclass calls pre_init instead of to_python, however to_python
93 is still necessary for Django's deserializer"""
96 def get_db_prep_value(self
, value
, connection
, prepared
=False):
97 """Convert JSON object to a string"""
98 if self
.null
and value
is None:
100 return json
.dumps(value
, **self
.dump_kwargs
)
102 def value_to_string(self
, obj
):
103 value
= self
._get_val_from_obj(obj
)
104 return self
.get_db_prep_value(value
, None)
106 def value_from_object(self
, obj
):
107 value
= super(JSONFieldBase
, self
).value_from_object(obj
)
108 if self
.null
and value
is None:
110 return self
.dumps_for_display(value
)
112 def dumps_for_display(self
, value
):
113 return json
.dumps(value
, **self
.dump_kwargs
)
115 def formfield(self
, **kwargs
):
117 if "form_class" not in kwargs
:
118 kwargs
["form_class"] = self
.form_class
120 field
= super(JSONFieldBase
, self
).formfield(**kwargs
)
122 if isinstance(field
, JSONFormFieldBase
):
123 field
.load_kwargs
= self
.load_kwargs
125 if not field
.help_text
:
126 field
.help_text
= "Enter valid JSON"
130 def get_default(self
):
132 Returns the default value for this field.
134 The default implementation on models.Field calls force_unicode
135 on the default, which means you can't set arbitrary Python
136 objects as the default. To fix this, we just return the value
137 without calling force_unicode on it. Note that if you set a
138 callable as a default, the field will still call it. It will
139 *not* try to pickle and encode it.
142 if self
.has_default():
143 if callable(self
.default
):
144 return self
.default()
145 return copy
.deepcopy(self
.default
)
146 # If the field doesn't have a default, then we punt to models.Field.
147 return super(JSONFieldBase
, self
).get_default()
149 class JSONField(JSONFieldBase
, models
.TextField
):
150 """JSONField is a generic textfield that serializes/deserializes JSON objects"""
151 form_class
= JSONFormField
153 def dumps_for_display(self
, value
):
154 kwargs
= { "indent": 2 }
155 kwargs
.update(self
.dump_kwargs
)
156 return json
.dumps(value
, **kwargs
)
159 class JSONCharField(JSONFieldBase
, models
.CharField
):
160 """JSONCharField is a generic textfield that serializes/deserializes JSON objects,
161 stored in the database like a CharField, which enables it to be used
162 e.g. in unique keys"""
163 form_class
= JSONCharFormField
167 from south
.modelsinspector
import add_introspection_rules
168 add_introspection_rules([], ["^jsonfield\.fields\.(JSONField|JSONCharField)"])