Files
edx-platform/common/djangoapps/util/json_request.py
Feanil Patel 9cf2f9f298 Run 2to3 -f future . -w
This will remove imports from __future__ that are no longer needed.

https://docs.python.org/3.5/library/2to3.html#2to3fixer-future
2019-12-30 10:35:30 -05:00

97 lines
3.8 KiB
Python

import decimal
import json
from functools import wraps
from django.core.serializers import serialize
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.query import QuerySet
from django.http import HttpResponse, HttpResponseBadRequest
class EDXJSONEncoder(DjangoJSONEncoder):
"""
Encoder for Decimal object, other objects will be encoded as per DjangoJSONEncoder default implementation.
NOTE:
Please see https://docs.djangoproject.com/en/1.8/releases/1.5/#system-version-of-simplejson-no-longer-used
DjangoJSONEncoder will now use the Python's json module but Python's json module don't know about how to
encode Decimal object, so as per default implementation Decimal objects will be encoded to `str` which we don't
want and also this is different from Django 1.4, In Django 1.4 if Decimal object has zeros after the decimal
point then object will be serialized as `int` else `float`, so we are keeping this behavior.
"""
def default(self, o): # pylint: disable=method-hidden
"""
Encode Decimal objects. If decimal object has zeros after the
decimal point then object will be serialized as `int` else `float`
"""
if isinstance(o, decimal.Decimal):
if o == o.to_integral():
return int(o)
return float(o)
else:
return super(EDXJSONEncoder, self).default(o)
def expect_json(view_function):
"""
View decorator for simplifying handing of requests that expect json. If the request's
CONTENT_TYPE is application/json, parses the json dict from request.body, and updates
request.POST with the contents.
"""
@wraps(view_function)
def parse_json_into_request(request, *args, **kwargs):
# cdodge: fix postback errors in CMS. The POST 'content-type' header can include additional information
# e.g. 'charset', so we can't do a direct string compare
if "application/json" in request.META.get('CONTENT_TYPE', '') and request.body:
try:
request.json = json.loads(request.body.decode('utf8'))
except ValueError:
return JsonResponseBadRequest({"error": "Invalid JSON"})
else:
request.json = {}
return view_function(request, *args, **kwargs)
return parse_json_into_request
class JsonResponse(HttpResponse):
"""
Django HttpResponse subclass that has sensible defaults for outputting JSON.
"""
def __init__(self, resp_obj=None, status=None, encoder=EDXJSONEncoder,
*args, **kwargs):
if resp_obj in (None, ""):
content = ""
status = status or 204
elif isinstance(resp_obj, QuerySet):
content = serialize('json', resp_obj)
else:
content = json.dumps(resp_obj, cls=encoder, indent=2, ensure_ascii=True)
kwargs.setdefault("content_type", "application/json")
if status:
kwargs["status"] = status
super(JsonResponse, self).__init__(content, *args, **kwargs)
class JsonResponseBadRequest(HttpResponseBadRequest):
"""
Subclass of HttpResponseBadRequest that defaults to outputting JSON.
Use this to send BadRequestResponse & some Json object along with it.
Defaults:
dictionary: empty dictionary
status: 400
encoder: DjangoJSONEncoder
"""
def __init__(self, obj=None, status=400, encoder=DjangoJSONEncoder, *args, **kwargs):
if obj in (None, ""):
content = ""
else:
content = json.dumps(obj, cls=encoder, indent=2, ensure_ascii=False)
kwargs.setdefault("content_type", "application/json")
kwargs["status"] = status
super(JsonResponseBadRequest, self).__init__(content, *args, **kwargs)