Added an optional LRU cache to the JSON encoder.

This commit is contained in:
Omer Katz 2014-10-02 17:18:32 +03:00
parent 79e91dff92
commit 3e69ed171d
3 changed files with 44 additions and 21 deletions

View File

@ -310,3 +310,16 @@ except ImportError:
klass.__unicode__ = klass.__str__ klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8') klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass return klass
try:
from functools import lru_cache
except ImportError:
try:
from backports.functools_lru_cache import lru_cache
except ImportError:
def lru_cache(maxsize=100, typed=False): # When the LRU cache decorator is not available replace with a stub.
def decorating_function(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
return decorating_function

View File

@ -111,7 +111,8 @@ DEFAULTS = {
'UNICODE_JSON': True, 'UNICODE_JSON': True,
'COMPACT_JSON': True, 'COMPACT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True, 'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True 'UPLOADED_FILES_USE_URL': True,
'ENCODER_LRU_CACHE_SIZE': 1024
} }

View File

@ -2,16 +2,19 @@
Helper classes for parsers. Helper classes for parsers.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db.models.query import QuerySet
from django.utils import six, timezone
from django.utils.datastructures import SortedDict
from django.utils.functional import Promise
from rest_framework.compat import force_text
import datetime import datetime
import decimal import decimal
import types import types
import json import json
from django.db.models.query import QuerySet
from django.utils import six, timezone
from django.utils.datastructures import SortedDict
from django.utils.functional import Promise
from rest_framework.compat import force_text, lru_cache
from rest_framework.settings import api_settings
class JSONEncoder(json.JSONEncoder): class JSONEncoder(json.JSONEncoder):
""" """
@ -19,11 +22,28 @@ class JSONEncoder(json.JSONEncoder):
decimal types, generators and other basic python objects. decimal types, generators and other basic python objects.
""" """
def default(self, obj): def default(self, obj):
# For Date Time string spec, see ECMA 262
# http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
if isinstance(obj, Promise): if isinstance(obj, Promise):
return force_text(obj) return force_text(obj)
elif isinstance(obj, datetime.datetime): elif isinstance(obj, QuerySet):
return tuple(obj)
elif hasattr(obj, '__getitem__'):
try:
return dict(obj)
except:
pass
elif hasattr(obj, 'tolist'):
# Numpy arrays and array scalars.
return obj.tolist()
elif hasattr(obj, '__iter__'):
return tuple(item for item in obj)
return self._default(obj)
@lru_cache(typed=True, maxsize=api_settings.ENCODER_LRU_CACHE_SIZE)
def _default(self, obj):
# For Date Time string spec, see ECMA 262
# http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
if isinstance(obj, datetime.datetime):
representation = obj.isoformat() representation = obj.isoformat()
if obj.microsecond: if obj.microsecond:
representation = representation[:23] + representation[26:] representation = representation[:23] + representation[26:]
@ -44,18 +64,7 @@ class JSONEncoder(json.JSONEncoder):
elif isinstance(obj, decimal.Decimal): elif isinstance(obj, decimal.Decimal):
# Serializers will coerce decimals to strings by default. # Serializers will coerce decimals to strings by default.
return float(obj) return float(obj)
elif isinstance(obj, QuerySet):
return tuple(obj)
elif hasattr(obj, 'tolist'):
# Numpy arrays and array scalars.
return obj.tolist()
elif hasattr(obj, '__getitem__'):
try:
return dict(obj)
except:
pass
elif hasattr(obj, '__iter__'):
return tuple(item for item in obj)
return super(JSONEncoder, self).default(obj) return super(JSONEncoder, self).default(obj)