19

Is there any simple way to override DjangoJSONEncoder.ensure_ascii and set it to False or output non-ascii text in django.http.JsonResponse in any other way?

4 Answers 4

26

As of Django 1.9 you can configure JSONResponse to disable the ensure_ascii switch, by passing in a value for the json_dumps_params argument:

return JsonResponse(response_data, safe=False, json_dumps_params={'ensure_ascii': False})

With ensure_ascii=False, json.dumps() outputs UTF-8 data for non-ASCII codepoints.

You could subclass JsonResponse to make it the default unless set differently:

from django.http.response import JsonResponse

class UTF8JsonResponse(JsonResponse):
    def __init__(self, *args, json_dumps_params=None, **kwargs):
        json_dumps_params = {"ensure_ascii": False, **(json_dumps_params or {})}
        super().__init__(*args, json_dumps_params=json_dumps_params, **kwargs)

then use that throughout instead of JsonResponse.

At the extreme end, you could monkey-patch the class to set ensure_ascii to False by default; put the following in a suitable module of your Django app (say, in a file named patches.py):

import logging
from functools import wraps
from django.http.response import JsonResponse

logger = logging.getLogger(__name__)

def patch_jsonresponse_disable_ensure_ascii():
    if getattr(JsonResponse, '_utf8_patched', False):
        # Already patched. Add warning in logs with stack to see what location
        # is trying to patch this a second time.
        logger.warning("JSONResponse UTF8 patch already applied", stack_info=True)
        return

    logger.debug("Patching JSONResponse to disable ensure_ascii")
    orig_init = JsonResponse.__init__

    @wraps(orig_init)
    def utf8_init(self, *args, json_dumps_params=None, **kwargs):
        json_dumps_params = {"ensure_ascii": False, **(json_dumps_params or {})}
        orig_init(self, *args, json_dumps_params=json_dumps_params, **kwargs)

    JsonResponse.__init__ = utf8_init
    JsonResponse._utf8_patched = True  # to prevent accidental re-patching

then import patch_jsonresponse_disable_ensure_ascii into your Django settings file and call it based on your desired config:

from yourapp.patches import patch_jsonresponse_disable_ensure_ascii

JSON_RESPONSES_UTF8 = True

if JSON_RESPONSES_UTF8:
    patch_jsonresponse_disable_ensure_ascii()
Sign up to request clarification or add additional context in comments.

2 Comments

Is there any setting item to set that to False golbally instead of put that in every JsonResponse?
@Jcyrss: expanded my answer to give you some more gradations, including monkeypatching Django itself to always set the flag to false.
11

EDIT:

Or if you tend to the utf-8 format, use instead of Django's JsonResponse():

return HttpResponse(json.dumps(response_data, ensure_ascii=False),
         content_type="application/json")

or

return JsonResponse(json.dumps(response_data, ensure_ascii=False), safe=False)

more about the safe=False HERE


OLD:

You don't have to whatever alter.

Although Django creates JSON data in ASCII (from UTF-8), Javascript will automatically decode it back to UTF-8.

3 Comments

Thanks for the suggestion, but I prefer readability over slightly better backward compatibility that I don't need at all in my case. Also, UTF-8 is recommended in RFC 7159.
JsonResponse(json.dumps(response_data, ensure_ascii=False), ...) doesn't work as this double-encodes the data. There is no point in s etting safe=False in that case either, as that flag applies to what is accepted when encoding the data to JSON. Since you already encoded all objects to a JSON string, there are no 'unsafe' objects to encode anymore.
For me the ensure_ascii=false was not enough: the response was still not perfect. I had to add the charset like this: return HttpResponse(json.dumps(response_data, ensure_ascii=False), content_type="application/json; charset=utf-8")
3
from django.core.serializers.json import DjangoJSONEncoder
from django.http import JsonResponse


class MyJsonResponse(JsonResponse):
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
        json_dumps_params = dict(ensure_ascii=False)
        super().__init__(data, encoder, safe, json_dumps_params, **kwargs)

1 Comment

This could be improved by allowing for json_dumps_params to be overridden.
1

I didn't find any better way yet than to utilize an already installed REST Framework:

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response

from .models import INITIATOR_TYPES

@api_view(['GET'])
@permission_classes((IsAuthenticatedOrReadOnly, ))
def initiator_types(request):
    data = {t[0]: str(t[1]) for t in INITIATOR_TYPES}
    return Response(data)

But I don't really like it. It's much more complicated than JsonResponse: https://stackoverflow.com/a/24411716/854477

2 Comments

You should put it as part of your original question.
It's a solution that I decided to share and simultaneously try searching for a better one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.