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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.