mise-en-production
[auf_framonde.git] / project / cmsplugin_contact_plus / jsonfield / fields.py
1 import copy
2 from django.db import models
3 from django.utils.translation import ugettext_lazy as _
4 try:
5 from django.utils import six
6 except ImportError:
7 import six
8
9 try:
10 import json
11 except ImportError:
12 from django.utils import simplejson as json
13
14 from django.forms import fields
15 try:
16 from django.forms.utils import ValidationError
17 except ImportError:
18 from django.forms.util import ValidationError
19
20 from .subclassing import SubfieldBase
21 from .encoder import JSONEncoder
22
23
24 class JSONFormFieldBase(object):
25
26 def to_python(self, value):
27 if isinstance(value, six.string_types):
28 try:
29 return json.loads(value, **self.load_kwargs)
30 except ValueError:
31 raise ValidationError(_("Enter valid JSON"))
32 return value
33
34 def clean(self, value):
35
36 if not value and not self.required:
37 return None
38
39 # Trap cleaning errors & bubble them up as JSON errors
40 try:
41 return super(JSONFormFieldBase, self).clean(value)
42 except TypeError:
43 raise ValidationError(_("Enter valid JSON"))
44
45
46 class JSONFormField(JSONFormFieldBase, fields.CharField):
47 pass
48
49 class JSONCharFormField(JSONFormFieldBase, fields.CharField):
50 pass
51
52
53 class JSONFieldBase(six.with_metaclass(SubfieldBase, models.Field)):
54
55 def __init__(self, *args, **kwargs):
56 self.dump_kwargs = kwargs.pop('dump_kwargs', {
57 'cls': JSONEncoder,
58 'separators': (',', ':')
59 })
60 self.load_kwargs = kwargs.pop('load_kwargs', {})
61
62 super(JSONFieldBase, self).__init__(*args, **kwargs)
63
64 def pre_init(self, value, obj):
65 """Convert a string value to JSON only if it needs to be deserialized.
66
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
69 deserialized"""
70
71 try:
72 if obj._state.adding:
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):
78 try:
79 return json.loads(value, **self.load_kwargs)
80 except ValueError:
81 raise ValidationError(_("Enter valid JSON"))
82
83 except AttributeError:
84 # south fake meta class doesn't create proper attributes
85 # see this:
86 # https://github.com/bradjasper/django-jsonfield/issues/52
87 pass
88
89 return value
90
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"""
94 return value
95
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:
99 return None
100 return json.dumps(value, **self.dump_kwargs)
101
102 def value_to_string(self, obj):
103 value = self._get_val_from_obj(obj)
104 return self.get_db_prep_value(value, None)
105
106 def value_from_object(self, obj):
107 value = super(JSONFieldBase, self).value_from_object(obj)
108 if self.null and value is None:
109 return None
110 return self.dumps_for_display(value)
111
112 def dumps_for_display(self, value):
113 return json.dumps(value, **self.dump_kwargs)
114
115 def formfield(self, **kwargs):
116
117 if "form_class" not in kwargs:
118 kwargs["form_class"] = self.form_class
119
120 field = super(JSONFieldBase, self).formfield(**kwargs)
121
122 if isinstance(field, JSONFormFieldBase):
123 field.load_kwargs = self.load_kwargs
124
125 if not field.help_text:
126 field.help_text = "Enter valid JSON"
127
128 return field
129
130 def get_default(self):
131 """
132 Returns the default value for this field.
133
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.
140
141 """
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()
148
149 class JSONField(JSONFieldBase, models.TextField):
150 """JSONField is a generic textfield that serializes/deserializes JSON objects"""
151 form_class = JSONFormField
152
153 def dumps_for_display(self, value):
154 kwargs = { "indent": 2 }
155 kwargs.update(self.dump_kwargs)
156 return json.dumps(value, **kwargs)
157
158
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
164
165
166 try:
167 from south.modelsinspector import add_introspection_rules
168 add_introspection_rules([], ["^jsonfield\.fields\.(JSONField|JSONCharField)"])
169 except ImportError:
170 pass