Skip to content

Commit

Permalink
Make JSONField works properly with Django 1.9
Browse files Browse the repository at this point in the history
  • Loading branch information
tkajtoch committed Dec 21, 2015
1 parent 1deeac9 commit 910a9c7
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 65 deletions.
3 changes: 2 additions & 1 deletion social/apps/django_app/default/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.contrib import admin

from social.utils import setting_name
from social.apps.django_app.default.compat import get_all_field_names_from_options
from social.apps.django_app.default.models import UserSocialAuth, Nonce, \
Association

Expand All @@ -24,7 +25,7 @@ def get_search_fields(self, request=None):
hasattr(_User, 'username') and 'username' or \
None
fieldnames = ('first_name', 'last_name', 'email', username)
all_names = _User._meta.get_all_field_names()
all_names = get_all_field_names_from_options(_User._meta)
search_fields = [name for name in fieldnames
if name and name in all_names]
return ['user__' + name for name in search_fields]
Expand Down
15 changes: 15 additions & 0 deletions social/apps/django_app/default/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django import VERSION


if VERSION >= (1, 8):
from itertools import chain

def get_all_field_names_from_options(options):
names = list(set(chain.from_iterable(
(field.name, field.attname) if hasattr(field, 'attname') else (field.name,)
for field in options.get_fields()
)))
return names
else:
def get_all_field_names_from_options(options):
return options.get_all_field_names()
162 changes: 98 additions & 64 deletions social/apps/django_app/default/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import six

from django import VERSION
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models

Expand All @@ -11,78 +12,111 @@
except ImportError:
from django.utils.encoding import smart_text

from social.utils import setting_name


if VERSION >= (1, 8):
_JSONField = models.TextField
_JSONFieldBase = models.Field
else:
_JSONField = six.with_metaclass(models.SubfieldBase, models.TextField)


class JSONField(_JSONField):
"""Simple JSON field that stores python structures as JSON strings
on database.
"""
_JSONFieldBase = six.with_metaclass(models.SubfieldBase, models.TextField)

def __init__(self, *args, **kwargs):
kwargs.setdefault('default', '{}')
super(JSONField, self).__init__(*args, **kwargs)

def from_db_value(self, value, expression, connection, context):
return self.to_python(value)

def to_python(self, value):
USE_POSTGRES_NATIVE_JSON_FIELD = getattr(settings, setting_name('USE_POSTGRES_NATIVE_JSON_FIELDL'), False)
if USE_POSTGRES_NATIVE_JSON_FIELD:
from django.contrib.postgres.fields import JSONField
else:
class JSONField(_JSONFieldBase):
"""
Convert the input JSON value into python structures, raises
django.core.exceptions.ValidationError if the data can't be converted.
Simple JSON field that stores python structures as JSON strings on database.
"""
if self.blank and not value:
return {}
value = value or '{}'
if isinstance(value, six.binary_type):
value = six.text_type(value, 'utf-8')
if isinstance(value, six.string_types):
try:
# with django 1.6 i have '"{}"' as default value here
if value[0] == value[-1] == '"':
value = value[1:-1]

return json.loads(value)
except Exception as err:
raise ValidationError(str(err))
else:
return value

def validate(self, value, model_instance):
"""Check value is a valid JSON string, raise ValidationError on
error."""
if isinstance(value, six.string_types):
super(JSONField, self).validate(value, model_instance)
try:
json.loads(value)
except Exception as err:
raise ValidationError(str(err))

def get_prep_value(self, value):
"""Convert value to JSON string before save"""
try:
return json.dumps(value)
except Exception as err:
raise ValidationError(str(err))
def __init__(self, *args, **kwargs):
kwargs.setdefault('default', {})
super(JSONField, self).__init__(*args, **kwargs)

def get_internal_type(self):
return 'TextField'

def to_python(self, value):
"""
Convert the input JSON value into python structures,
raises django.core.exceptions.ValidationError if the data can't be converted.
"""

if self.blank and not value:
return {}

value = value or '{}'
if isinstance(value, six.binary_type):
value = six.text_type(value, 'utf-8')

if isinstance(value, six.string_types):
try:
# with django 1.6 i have '"{}"' as default value here
if value[0] == value[-1] == '"':
value = value[1:-1]

return json.loads(value)
except Exception as err:
raise ValidationError(str(err))
else:
return value

# Support for Django >= 1.8
def from_db_value(self, value, expression, connection, context):
"""
Convert the input JSON value into python structures, raises
django.core.exceptions.ValidationError if the data can't be converted.
"""

if self.blank and not value:
return {}

value = value or '{}'
if isinstance(value, six.binary_type):
value = six.text_type(value, 'utf-8')

if isinstance(value, six.string_types):
try:
# with django 1.6 i have '"{}"' as default value here
if value[0] == value[-1] == '"':
value = value[1:-1]

return json.loads(value)
except Exception as err:
raise ValidationError(str(err))
else:
return value

def validate(self, value, model_instance):
"""Check value is a valid JSON string, raise ValidationError on
error."""
if isinstance(value, six.string_types):
super(JSONField, self).validate(value, model_instance)
try:
json.loads(value)
except Exception as err:
raise ValidationError(str(err))

def get_prep_value(self, value):
"""
Convert value to JSON string vefore save
"""

def value_to_string(self, obj):
"""Return value from object converted to string properly"""
return smart_text(self.get_prep_value(self._get_val_from_obj(obj)))
try:
return json.dumps(value)
except Exception as e:
raise ValidationError(str(e))

def value_from_object(self, obj):
"""Return value dumped to string."""
return self.get_prep_value(self._get_val_from_obj(obj))
def value_to_string(self, obj):
"""
Return value from object converted to string properly
"""

return smart_text(self.get_prep_value(self._get_val_from_obj(obj)))

try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules(
[],
["^social\.apps\.django_app\.default\.fields\.JSONField"]
)
except:
pass
def value_from_object(self, obj):
"""
Return value dumped to string
"""

return self.get_prep_value(self._get_val_from_obj(obj))

0 comments on commit 910a9c7

Please sign in to comment.