Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fixed #17942 - added JsonResponse class #1182

Closed
wants to merge 1 commit into from

8 participants

django/http/response.py
@@ -33,6 +35,7 @@ class HttpResponseBase(six.Iterator):
"""
status_code = 200
+ _default_content_type = settings.DEFAULT_CONTENT_TYPE
@charettes Collaborator

Instead of adding this extra attribute you should set 'application/json' as the default content_type in the JSONResponse.__init__.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@charettes charettes commented on the diff
django/http/response.py
@@ -448,3 +451,27 @@ class HttpResponseServerError(HttpResponse):
class Http404(Exception):
pass
+
+
+class JsonResponse(HttpResponse):
@charettes Collaborator

The class should really be named JSONResponse.

@lukaszb
lukaszb added a note

Sure about that? It makes sense if we look at the encoder classes, however it doesn't fit well if put next to HttpResponse.

@charettes Collaborator

Oh I see why you chose Json instead.

A quick grep reveals that Http/Json/Xml/Html and their uppercase counterpart are used inconsistently across code base.

I'd weight in favor of using JSON instead but it shouldn't block the addition of this feature.

@mjtamlyn Collaborator

I think although JSON is better, I'd rather this was consitent with all of the other HttpResponse classes. Doesn't make the correct, but consistence should come first here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((10 lines not shown))
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. It can
+ be changed via :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY`.
+ :param encoder: Should be an json encoder class. Defaults to
+ class pointed by :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY`.
+ """
+ _default_content_type = 'application/json'
+
+ def __init__(self, data, encoder=None, **kwargs):
+ if settings.JSON_RESPONSE_ALLOW_DICTS_ONLY and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized, please change JSON_RESPONSE_DEFAULT_ENCODER '
+ 'setting to False')
+ if encoder is None:
+ encoder = import_by_path(settings.JSON_RESPONSE_DEFAULT_ENCODER)
+ data = json.dumps(data, cls=encoder, ensure_ascii=False)
@charettes Collaborator

Related to the _default_content_type comment: kwargs.setdefault('content_type', 'application/json').

@charettes Collaborator

Why are you setting ensure_ascii to False?

@alex Collaborator
alex added a note

Rather than using dumps, you can do the super first, and then json.dump(self, data, ...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((4 lines not shown))
+
+
+class JsonResponse(HttpResponse):
+ """
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. It can
+ be changed via :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY`.
+ :param encoder: Should be an json encoder class. Defaults to
+ class pointed by :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY`.
+ """
+ _default_content_type = 'application/json'
+
+ def __init__(self, data, encoder=None, **kwargs):
+ if settings.JSON_RESPONSE_ALLOW_DICTS_ONLY and not isinstance(data, dict):
@charettes Collaborator

As highlighted by erikr on Trac this shouldn't need an extra setting. A safe kwarg defaulting to True should do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((8 lines not shown))
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. It can
+ be changed via :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY`.
+ :param encoder: Should be an json encoder class. Defaults to
+ class pointed by :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY`.
+ """
+ _default_content_type = 'application/json'
+
+ def __init__(self, data, encoder=None, **kwargs):
+ if settings.JSON_RESPONSE_ALLOW_DICTS_ONLY and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized, please change JSON_RESPONSE_DEFAULT_ENCODER '
+ 'setting to False')
+ if encoder is None:
@charettes Collaborator

Here I think we just should just default to json.dumps if no encoder is specified. No need for an extra setting.

@charettes Collaborator

My last comment might be confusing. I meant that you should let the encoder class (the json.dump cls argument) default to None.

@mjtamlyn Collaborator

I can see a decent reason to use the DjangoJSONEncoder as the default as it gives us Decimal and datetime support for free. These are very common use cases for django sites (especially datetime!).

@charettes Collaborator

Agreed DjangoJSONEncoder would be better default encoder class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -800,6 +800,53 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.6
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's super class but has some format specific
@mjtamlyn Collaborator

superclass is one word

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((4 lines not shown))
+JsonResponse objects
+====================
+
+.. versionadded:: 1.6
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's super class but has some format specific
+changes:
+
+* First argument can be any JSON-serializable object, in example a ``dict``,
+ ``list`` etc. (unless :setting:`JSON_RESPONSE_ALLOW_DICTS_ONLY` is set to
+ ``True`` in which case, only ``dict`` objects are allowed)
+* It's default *Content-Type* header is set to ``application/json`` (and proper
+ ``charset``)
@mjtamlyn Collaborator

What is the proper charset?

@lukaszb
lukaszb added a note

settings.DEFAULT_CHARSET

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/conf/global_settings.py
@@ -547,6 +547,13 @@
CSRF_COOKIE_SECURE = False
CSRF_COOKIE_HTTPONLY = False
+########
+# JSON #
+########
+
+JSON_RESPONSE_ALLOW_DICTS_ONLY = True
+JSON_RESPONSE_DEFAULT_ENCODER = 'django.core.serializers.json.DjangoJSONEncoder'
@alex Collaborator
alex added a note

I know there was a already a conversation about this, but I'm strongly opposed to using settings for these. They should be options on the JSONResponse constructor, there's no need to bring new global state into this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -800,6 +800,74 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.6
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
+ to ``False`` (see below) it can be any JSON-serializable object.
+* It's default *Content-Type* header is set to ``application/json`` (and proper
@claudep Collaborator
claudep added a note

Typo: It's -> Its

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((45 lines not shown))
+parameter to ``False``::
+
+ >>> data = ['foo', 'bar']
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
+
+.. warning::
+
+ Before `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison ``Array`` constructor. That's why by default
+ Django does not allow to pass non-dict objects to
+ :class:`django.http.JsonResponse` constructor. However, most modern
+ browsers already implement EcmaScript 5, which removes this attack vector.
+ Therefor it is possible to disable this security check.
@claudep Collaborator
claudep added a note

Typo: Therefore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@claudep
Collaborator

Tests do not pass on Python 3. json.loads does not support bytestrings, so you should simply replace json.loads(response.content) by json.loads(response.content.decode()).

docs/ref/request-response.txt
((32 lines not shown))
+
+Typical usage could look like::
+
+ >>> data = {'foo': 'bar'}
+ >>> response = JsonResponse(data)
+ >>> response.content
+ '{"foo": "bar"}'
+
+
+Serialize non-dictionary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to serialize objects other than ``dict`` you must set ``safe``
+parameter to ``False``::
+
+ >>> data = ['foo', 'bar']
@mjtamlyn Collaborator

What is the purpose of this data line?

@lukaszb
lukaszb added a note

Oups, initially I had much longer examples and forgot to remove them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mjtamlyn
Collaborator

I would prefer these tests to be made a bit more "unity". The majority of the views simply return a certain response, I don't think that they need to be hooked in to a url. When these tests are run the code goes through all the middleware etc which is probably unneeded. Whilst there will not be a significant speed different here, in order to understand what the test is doing you currently need three files open - the test file, the urls file and the views file. The tests themselves would then perhaps be better living in the responses folder.

Sorry if I'm nitpicking.

@lukaszb

Yep, I see your point. I decided to put tests at views as most of them are kind of integration tests. At one point I wanted to move most of them to the httpwrappers and left one or two at view_tests. Then, however, we would and up with 4 files ;-)

There is possibility to put all the tests at httpwareppers as suggested. Tests would be much easier to read, indeed, but there would be no test that checks if whole request-response cycle works for JsonResponse.

So, let me know what you think. As for me - if we decide to put tests into httpwrappers I would rather leave one or two tests at view tests.

@mjtamlyn
Collaborator

Seems reasonable to leave one test in view_tests to test the full response cycle, especially if that example is also in the unit tests. Testing that options like safe work as documented don't really need to be full stack tests, and it can be clearer to understand what the test is doing.

django/http/response.py
((15 lines not shown))
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
+ # (serializers module imports db package)
+ from django.core.serializers.json import DjangoJSONEncoder
+ if safe and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized set safe parameter to False')
+ default_content_type = 'application/json; charset=%s' % settings.DEFAULT_CHARSET
+ kwargs.setdefault('content_type', default_content_type)
+ if encoder is None:
+ encoder = DjangoJSONEncoder
+ data = json.dumps(data, cls=encoder)
@charettes Collaborator

Was there any further discussion concerning using json.dump as @alex suggested instead? I guess this should be considered a StreamingJsonResponse if we use it?

@lukaszb
lukaszb added a note

Yep, sorry, missed that in the first place. According to the docs passing iterators is deprecated. Am not sure about file-like objects as it is also stated that since 1.5 responses could consume them lazily. Calling json.dumps is more explicit here, imho.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((7 lines not shown))
+ """
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter detail for more information.
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
+ # (serializers module imports db package)
+ from django.core.serializers.json import DjangoJSONEncoder
@claudep Collaborator
claudep added a note

I've just committed some changes to django.db importability. Could you try to move the import at the top to see if it works now?

@lukaszb
lukaszb added a note

Done, thanks. And yeah, it works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
+import json
@timgraham Owner

Please alphabetize imports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
@@ -456,3 +458,31 @@ class HttpResponseServerError(HttpResponse):
class Http404(Exception):
pass
+
+
+class JsonResponse(HttpResponse):
+ """
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter detail for more information.
@timgraham Owner

chop "detail"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
@@ -456,3 +458,31 @@ class HttpResponseServerError(HttpResponse):
class Http404(Exception):
pass
+
+
+class JsonResponse(HttpResponse):
+ """
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter detail for more information.
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
@timgraham Owner

Controls*

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((5 lines not shown))
+
+class JsonResponse(HttpResponse):
+ """
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter detail for more information.
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
@timgraham Owner

remove out of date comment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((9 lines not shown))
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter detail for more information.
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
+ # (serializers module imports db package)
+ if safe and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized set safe parameter to False')
@timgraham Owner

set the safe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((16 lines not shown))
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
+ # (serializers module imports db package)
+ if safe and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized set safe parameter to False')
+ default_content_type = 'application/json; charset=%s' % settings.DEFAULT_CHARSET
+ kwargs.setdefault('content_type', default_content_type)
+ if encoder is None:
+ encoder = DjangoJSONEncoder
+ data = json.dumps(data, cls=encoder)
+ super(JsonResponse, self).__init__(data, **kwargs)
+
@timgraham Owner

chop trailing newline (check code with flake8 to see a couple other minor formatting things)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
@timgraham Owner

include __init__ signature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham timgraham commented on the diff
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
@timgraham Owner

`` around JsonResponse

@lukaszb
lukaszb added a note

Is it necessary? Can't see `` around HttpResponse in other headers.

@timgraham Owner

Ok, I can do it in a separate commit or create a ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
@timgraham Owner

A HttpResponse subclass... "a JSON-encoded response" or "JSON-encoded responses".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
@timgraham Owner

It inherits most behavior from its superclass with a couple differences:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
@timgraham Owner

The first argument...
If the safe parameter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
+ to ``False`` (see below) it can be any JSON-serializable object.
+* Its default *Content-Type* header is set to ``application/json`` (and proper
@timgraham Owner

`` around Content-Type rather than *?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
@@ -825,6 +825,72 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
+ to ``False`` (see below) it can be any JSON-serializable object.
+* Its default *Content-Type* header is set to ``application/json`` (and proper
+ ``charset``, see :setting:`DEFAULT_CHARSET` and :meth:`HttpResponse.__init__`
@timgraham Owner

"and includes the :setting:DEFAULT_CHARSET"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((5 lines not shown))
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
+ to ``False`` (see below) it can be any JSON-serializable object.
+* Its default *Content-Type* header is set to ``application/json`` (and proper
+ ``charset``, see :setting:`DEFAULT_CHARSET` and :meth:`HttpResponse.__init__`
+ for information on how this header is constructed)
+* Accepts ``encoder`` parameter. Defaults to
@timgraham Owner

The encoder will be used the serialized the data. Defaults to...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((10 lines not shown))
+
+:class:`HttpResponse` subclass that helps to create JSON-encoded response.
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
+ to ``False`` (see below) it can be any JSON-serializable object.
+* Its default *Content-Type* header is set to ``application/json`` (and proper
+ ``charset``, see :setting:`DEFAULT_CHARSET` and :meth:`HttpResponse.__init__`
+ for information on how this header is constructed)
+* Accepts ``encoder`` parameter. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``
+
+ .. seealso:: :ref:`JSON serialization <serialization-formats_json>`
+
+* Accepts ``safe`` boolean parameter. Defaults to ``True``. If set to ``False``,
@timgraham Owner

The safe boolean parameter default to True. If it's set to False, ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((12 lines not shown))
+Essentially it is very similar to it's superclass but has some format specific
+changes:
+
+* First argument should be a ``dict`` instance. If ``safe`` parameter is set
+ to ``False`` (see below) it can be any JSON-serializable object.
+* Its default *Content-Type* header is set to ``application/json`` (and proper
+ ``charset``, see :setting:`DEFAULT_CHARSET` and :meth:`HttpResponse.__init__`
+ for information on how this header is constructed)
+* Accepts ``encoder`` parameter. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``
+
+ .. seealso:: :ref:`JSON serialization <serialization-formats_json>`
+
+* Accepts ``safe`` boolean parameter. Defaults to ``True``. If set to ``False``,
+ any object can be passed for serialization (otherwise only ``dict`` instances
+ are allowed). If ``safe`` is ``True`` and non-``dict`` object is passed as
@timgraham Owner

a non-dict object is passed as the first argument, a :exc:~exceptions.TypeError will be raised.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((20 lines not shown))
+* Accepts ``encoder`` parameter. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``
+
+ .. seealso:: :ref:`JSON serialization <serialization-formats_json>`
+
+* Accepts ``safe`` boolean parameter. Defaults to ``True``. If set to ``False``,
+ any object can be passed for serialization (otherwise only ``dict`` instances
+ are allowed). If ``safe`` is ``True`` and non-``dict`` object is passed as
+ first argument, ``TypeError`` would be raised.
+
+Usage
+-----
+
+Typical usage could look like::
+
+ >>> response = JsonResponse({'foo': 'bar'})
@timgraham Owner

including import for JsonResponse wouldn't hurt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((25 lines not shown))
+* Accepts ``safe`` boolean parameter. Defaults to ``True``. If set to ``False``,
+ any object can be passed for serialization (otherwise only ``dict`` instances
+ are allowed). If ``safe`` is ``True`` and non-``dict`` object is passed as
+ first argument, ``TypeError`` would be raised.
+
+Usage
+-----
+
+Typical usage could look like::
+
+ >>> response = JsonResponse({'foo': 'bar'})
+ >>> response.content
+ '{"foo": "bar"}'
+
+
+Serialize non-dictionary objects
@timgraham Owner

Serializing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((28 lines not shown))
+ first argument, ``TypeError`` would be raised.
+
+Usage
+-----
+
+Typical usage could look like::
+
+ >>> response = JsonResponse({'foo': 'bar'})
+ >>> response.content
+ '{"foo": "bar"}'
+
+
+Serialize non-dictionary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to serialize objects other than ``dict`` you must set ``safe``
@timgraham Owner

the safe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((33 lines not shown))
+Typical usage could look like::
+
+ >>> response = JsonResponse({'foo': 'bar'})
+ >>> response.content
+ '{"foo": "bar"}'
+
+
+Serialize non-dictionary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to serialize objects other than ``dict`` you must set ``safe``
+parameter to ``False``::
+
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
@timgraham Owner

Without passing safe=False, a TypeError will be raised.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((37 lines not shown))
+ '{"foo": "bar"}'
+
+
+Serialize non-dictionary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to serialize objects other than ``dict`` you must set ``safe``
+parameter to ``False``::
+
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
+
+.. warning::
+
+ Before `5th edition of EcmaScript
@timgraham Owner

Before the

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((39 lines not shown))
+
+Serialize non-dictionary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to serialize objects other than ``dict`` you must set ``safe``
+parameter to ``False``::
+
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
+
+.. warning::
+
+ Before `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison ``Array`` constructor. That's why by default
@timgraham Owner

the JavaScript Array constructor. For this reason, Django does not allow passing non-dict objects to the JsonResponse constructor by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((42 lines not shown))
+
+In order to serialize objects other than ``dict`` you must set ``safe``
+parameter to ``False``::
+
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
+
+.. warning::
+
+ Before `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison ``Array`` constructor. That's why by default
+ Django does not allow to pass non-dict objects to
+ :class:`django.http.JsonResponse` constructor. However, most modern
+ browsers already implement EcmaScript 5, which removes this attack vector.
@timgraham Owner

chop "already"?
chop comma

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((44 lines not shown))
+parameter to ``False``::
+
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
+
+.. warning::
+
+ Before `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison ``Array`` constructor. That's why by default
+ Django does not allow to pass non-dict objects to
+ :class:`django.http.JsonResponse` constructor. However, most modern
+ browsers already implement EcmaScript 5, which removes this attack vector.
+ Therefore it is possible to disable this security check.
+
@timgraham Owner

chop newline here and at end of next section (just 1 newline between sections in docs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((46 lines not shown))
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False`` explicitly, ``TypeError`` would be raised.
+
+.. warning::
+
+ Before `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison ``Array`` constructor. That's why by default
+ Django does not allow to pass non-dict objects to
+ :class:`django.http.JsonResponse` constructor. However, most modern
+ browsers already implement EcmaScript 5, which removes this attack vector.
+ Therefore it is possible to disable this security check.
+
+
+Changing default JSON encoder
@timgraham Owner

the default

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/ref/request-response.txt
((49 lines not shown))
+
+.. warning::
+
+ Before `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison ``Array`` constructor. That's why by default
+ Django does not allow to pass non-dict objects to
+ :class:`django.http.JsonResponse` constructor. However, most modern
+ browsers already implement EcmaScript 5, which removes this attack vector.
+ Therefore it is possible to disable this security check.
+
+
+Changing default JSON encoder
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you need to use non-default encoder class you could pass ``encoder``
@timgraham Owner

could->can
pass the encoder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
docs/releases/1.7.txt
@@ -216,6 +216,17 @@ example ``qs.filter(author__birthdate__year__lte=1981)``.
For more information about both custom lookups and transforms refer to
:doc:`custom lookups </ref/models/custom-lookups>` documentation.
+
+``JsonResponse``
+~~~~~~~~~~~~~~~~
+
+A new :class:`django.http.JsonResponse` subclass of
@timgraham Owner

I would rename the "Requests" section of "Minor Features" to "Requests and Responses" and put this sentence there. I would say something more descriptive like "The JsonResponse helps easily create JSON-encoded responses".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/httpwrappers/tests.py
@@ -450,6 +452,31 @@ def test_not_allowed(self):
content_type='text/html')
self.assertContains(response, 'Only the GET method is allowed', status_code=405)
+class JsonResponseTests(TestCase):
+ def test_json_response_non_ascii(self):
+ data = {'key': 'łóżko'}
+ response = JsonResponse(data)
+ self.assertEqual(json.loads(response.content.decode()), data)
+
+ def test_json_response_raises_type_error_with_default_setting(self):
+ with self.assertRaises(TypeError):
@timgraham Owner

consider assertRaisesMessage so you can check the error message as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/view_tests/tests/test_json.py
@@ -0,0 +1,21 @@
+# encoding: UTF-8
@timgraham Owner

"utf8" appears to be most commonly used

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/view_tests/tests/test_json.py
@@ -0,0 +1,21 @@
+# encoding: UTF-8
+from __future__ import unicode_literals, absolute_import
+
+import json
@timgraham Owner

newline after json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/view_tests/tests/test_json.py
((1 lines not shown))
+# encoding: UTF-8
+from __future__ import unicode_literals, absolute_import
+
+import json
+from django.test import TestCase
+from django.test.utils import override_settings
+
+
+class JsonResponseTests(TestCase):
+ urls = 'view_tests.generic_urls'
+
+ @override_settings(DEFAULT_CHARSET='utf8')
+ def test_json_response(self):
+ response = self.client.get('/json/response/')
+ self.assertEqual(response.status_code, 200)
+ expected = 'application/json; charset=utf8'
@timgraham Owner

I'd chop the intermediate variable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@lukaszb

@timgraham, really appreciate your review. Hope I haven't missed anything. And couldn't get rid of that:

tests/httpwrappers/tests.py:465:13: E125 continuation line does not distinguish itself from next logical line
docs/releases/1.7.txt
@@ -253,6 +253,9 @@ For more information about both custom lookups and transforms refer to
Minor features
~~~~~~~~~~~~~~
+Minor features
@timgraham Owner

Remove

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham
Owner

There are a couple unaddressed comments in django/http/response.py (you'll see that are collapsed above). Would be great if you could rebase this and squash into a single commit. Might be easiest to get the current diff and apply it to master rather than trying to rebase the current branch as I think there will be a lot of conflicts if you use the latter method. Thanks.

django/http/response.py
((10 lines not shown))
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter detail for more information.
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
+ # (serializers module imports db package)
+ if safe and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized set the safe parameter to False')
+ default_content_type = 'application/json; charset=%s' % settings.DEFAULT_CHARSET
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@tomchristie tomchristie commented on the diff
tests/view_tests/views.py
@@ -334,3 +336,13 @@ def multivalue_dict_key_error(request):
exc_info = sys.exc_info()
send_log(request, exc_info)
return technical_500_response(request, *exc_info)
+
+
+def json_response_view(request):
+ return JsonResponse({
+ 'a': [1, 2, 3],
+ 'foo': {'bar': 'baz'},
+ # Make sure datetime and Decimal objects would be serialized properly
+ 'timestamp': datetime.datetime(2013, 5, 19, 20),
+ 'value': decimal.Decimal('3.14'),
+ })

May as well put in the newline here.

@lukaszb
lukaszb added a note

I'm not sure what do you mean by that? New line at the end of the file? Or newline character inside response?

@timgraham Owner

Yes, add the newline at the EOF (I think it's a flake8 error not to have it?).

@lukaszb
lukaszb added a note

Hmm, I believe it's the other way round (flake8 warns about extra newline at the end of the file...)

@timgraham Owner

There should be 1 newline (the little icon that github displays shouldn't appear).

@lukaszb
lukaszb added a note

Ok, can do that. Just want to make 100% sure of this. Have just looked into some random files within Django (contrib/admin, core/urlresolvers) and there are no newlines at the end of the files (not a single one). And when I add that extra newline, flake8 warns me about this.

You'd still want me to add that newline?

@charettes Collaborator

@lukaszb which contrib/admin file are you talking about? Notice how contrib/admin/options.py doesn't have the missing new line icon.

Make sure not to add two newlines, just put your cursor right where the missing new line icon is and press ENTER once.

@timgraham Owner

If I pull your branch I get this error: "./tests/view_tests/views.py:348:7: W292 no newline at end of file"

That's different from adding a blank line at the of file which results in an error like this: "./tests/view_tests/generic_urls.py:64:1: W391 blank line at end of file"

@lukaszb
lukaszb added a note

Right... my bad, totally forgotten about my vim config related with newlines at the end of the file. Should be fixed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
django/http/response.py
((13 lines not shown))
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controlls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=None, safe=True, **kwargs):
+ # Need to import encoder here in order to prevent import errors
+ # (serializers module imports db package)
+ if safe and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized set the safe parameter to False')
+ default_content_type = 'application/json; charset=%s' % settings.DEFAULT_CHARSET
+ kwargs.setdefault('content_type', default_content_type)
+ if encoder is None:
+ encoder = DjangoJSONEncoder

Maybe just set that as the default in the signature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@timgraham
Owner

Thanks, merged in 0242134 with minor edits.

@timgraham timgraham closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 14, 2014
  1. @lukaszb
This page is out of date. Refresh to see the latest.
View
4 django/http/__init__.py
@@ -5,7 +5,7 @@
HttpResponseRedirect, HttpResponsePermanentRedirect,
HttpResponseNotModified, HttpResponseBadRequest, HttpResponseForbidden,
HttpResponseNotFound, HttpResponseNotAllowed, HttpResponseGone,
- HttpResponseServerError, Http404, BadHeaderError)
+ HttpResponseServerError, Http404, BadHeaderError, JsonResponse)
from django.http.utils import (fix_location_header,
conditional_content_removal, fix_IE_for_attach, fix_IE_for_vary)
@@ -16,6 +16,6 @@
'HttpResponsePermanentRedirect', 'HttpResponseNotModified',
'HttpResponseBadRequest', 'HttpResponseForbidden', 'HttpResponseNotFound',
'HttpResponseNotAllowed', 'HttpResponseGone', 'HttpResponseServerError',
- 'Http404', 'BadHeaderError', 'fix_location_header',
+ 'Http404', 'BadHeaderError', 'fix_location_header', 'JsonResponse',
'conditional_content_removal', 'fix_IE_for_attach', 'fix_IE_for_vary',
]
View
26 django/http/response.py
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
import datetime
-import time
+import json
import sys
+import time
from email.header import Header
try:
from urllib.parse import urlparse
@@ -13,6 +14,7 @@
from django.core import signals
from django.core import signing
from django.core.exceptions import DisallowedRedirect
+from django.core.serializers.json import DjangoJSONEncoder
from django.http.cookie import SimpleCookie
from django.utils import six, timezone
from django.utils.encoding import force_bytes, force_text, iri_to_uri
@@ -456,3 +458,25 @@ class HttpResponseServerError(HttpResponse):
class Http404(Exception):
pass
+
+
+class JsonResponse(HttpResponse):
@charettes Collaborator

The class should really be named JSONResponse.

@lukaszb
lukaszb added a note

Sure about that? It makes sense if we look at the encoder classes, however it doesn't fit well if put next to HttpResponse.

@charettes Collaborator

Oh I see why you chose Json instead.

A quick grep reveals that Http/Json/Xml/Html and their uppercase counterpart are used inconsistently across code base.

I'd weight in favor of using JSON instead but it shouldn't block the addition of this feature.

@mjtamlyn Collaborator

I think although JSON is better, I'd rather this was consitent with all of the other HttpResponse classes. Doesn't make the correct, but consistence should come first here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ """
+ An HTTP response class that consumes data to be serialized.
+
+ :param data: Data to be dumped into json. By default only ``dict`` objects
+ are allowed to be passed due to security flaw before EcmaScript 5. See
+ safe parameter for more information.
+ :param encoder: Should be an json encoder class. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``.
+ :param safe: Controls if only ``dict`` objects can be serialized. Defaults
+ to ``True``.
+ """
+
+ def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
+ if safe and not isinstance(data, dict):
+ raise TypeError('In order to allow non-dict objects to be '
+ 'serialized set the safe parameter to False')
+ kwargs.setdefault('content_type', 'application/json')
+ data = json.dumps(data, cls=encoder)
+ super(JsonResponse, self).__init__(content=data, **kwargs)
View
67 docs/ref/request-response.txt
@@ -825,6 +825,73 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. _httpresponse-streaming:
+JsonResponse objects
@timgraham Owner

`` around JsonResponse

@lukaszb
lukaszb added a note

Is it necessary? Can't see `` around HttpResponse in other headers.

@timgraham Owner

Ok, I can do it in a separate commit or create a ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+====================
+
+.. versionadded:: 1.7
+
+.. class:: JsonResponse
@timgraham Owner

include __init__ signature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+.. method:: JsonResponse.__init__(data, encoder=None, safe=True, **kwargs)
+
+ :class:`HttpResponse` subclass that helps to create JSON-encoded response.
+ It inherits most behavior from its superclass with a couple differences:
+
+ Its default ``Content-Type`` header is set to ``application/json``.
+
+ The first parameter,``data``, should be a ``dict`` instance. If the ``safe``
+ parameter is set to ``False`` (see below) it can be any JSON-serializable
+ object.
+
+ The ``encoder`` will be used to serialize the data. Defaults to
+ ``django.core.serializers.json.DjangoJSONEncoder``
+
+ .. seealso:: :ref:`JSON serialization <serialization-formats_json>`
+
+ The ``safe`` boolean parameter defaults to ``True``. If it's set to ``False``,
+ any object can be passed for serialization (otherwise only ``dict`` instances
+ are allowed). If ``safe`` is ``True`` and a non-``dict`` object is passed as
+ the first argument, a :exc:`~exceptions.TypeError` will be raised.
+
+Usage
+-----
+
+Typical usage could look like::
+
+ >>> from django.http.response import JsonResponse
+ >>> response = JsonResponse({'foo': 'bar'})
+ >>> response.content
+ '{"foo": "bar"}'
+
+
+Serializing non-dictionary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to serialize objects other than ``dict`` you must set the ``safe``
+parameter to ``False``::
+
+ >>> response = JsonResponse([1, 2, 3], safe=False)
+
+Without passing ``safe=False``, a :exc:`~exceptions.TypeError` will be raised.
+
+.. warning::
+
+ Before the `5th edition of EcmaScript
+ <http://www.ecma-international.org/publications/standards/Ecma-262.htm>`_
+ it was possible to poison the Javascript ``Array`` constructor. For this
+ reason, Django does not allow passing non-dict objects to the
+ :class:`django.http.JsonResponse` constructor by default. However, most
+ modern browsers implement EcmaScript 5 which removes this attack vector.
+ Therefore it is possible to disable this security check.
+
+Changing default JSON encoder
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you need to use non-default encoder class you can pass the ``encoder``
+parameter to the constructor method::
+
+ >>> response = JsonResponse(data, encoder=MyJSONEncoder)
+
StreamingHttpResponse objects
=============================
View
8 docs/releases/1.7.txt
@@ -671,12 +671,16 @@ Templates
* The new :tfilter:`truncatechars_html` filter truncates a string to be no
longer than the specified number of characters, taking HTML into account.
-Requests
-^^^^^^^^
+Requests and Responses
+^^^^^^^^^^^^^^^^^^^^^^
* The new :attr:`HttpRequest.scheme <django.http.HttpRequest.scheme>` attribute
specifies the scheme of the request (``http`` or ``https`` normally).
+* A new :class:`django.http.JsonResponse` subclass of
+ :class:`django.http.HttpResponse` that helps easily create JSON-encoded
+ responses.
+
Tests
^^^^^
View
2  docs/topics/serialization.txt
@@ -216,6 +216,8 @@ the auth.User model has such a relation to the auth.Permission model::
This example links the given user with the permission models with PKs 46 and 47.
+.. _serialization-formats_json:
+
JSON
~~~~
View
33 tests/httpwrappers/tests.py
@@ -2,18 +2,20 @@
from __future__ import unicode_literals
import copy
+import json
import os
import pickle
import unittest
import warnings
from django.core.exceptions import SuspiciousOperation
+from django.core.serializers.json import DjangoJSONEncoder
from django.core.signals import request_finished
from django.db import close_old_connections
from django.http import (QueryDict, HttpResponse, HttpResponseRedirect,
HttpResponsePermanentRedirect, HttpResponseNotAllowed,
HttpResponseNotModified, StreamingHttpResponse,
- SimpleCookie, BadHeaderError,
+ SimpleCookie, BadHeaderError, JsonResponse,
parse_cookie)
from django.test import TestCase
from django.utils.encoding import smart_str, force_text
@@ -451,6 +453,35 @@ def test_not_allowed(self):
self.assertContains(response, 'Only the GET method is allowed', status_code=405)
+class JsonResponseTests(TestCase):
+ def test_json_response_non_ascii(self):
+ data = {'key': 'łóżko'}
+ response = JsonResponse(data)
+ self.assertEqual(json.loads(response.content.decode()), data)
+
+ def test_json_response_raises_type_error_with_default_setting(self):
+ with self.assertRaisesMessage(TypeError,
+ 'In order to allow non-dict objects to be serialized set the safe '
+ 'parameter to False'):
+ JsonResponse([1, 2, 3])
+
+ def test_json_response_text(self):
+ response = JsonResponse('foobar', safe=False)
+ self.assertEqual(json.loads(response.content.decode()), 'foobar')
+
+ def test_json_response_list(self):
+ response = JsonResponse(['foo', 'bar'], safe=False)
+ self.assertEqual(json.loads(response.content.decode()), ['foo', 'bar'])
+
+ def test_json_response_custom_encoder(self):
+ class CustomDjangoJSONEncoder(DjangoJSONEncoder):
+ def encode(self, o):
+ return json.dumps({'foo': 'bar'})
+
+ response = JsonResponse({}, encoder=CustomDjangoJSONEncoder)
+ self.assertEqual(json.loads(response.content.decode()), {'foo': 'bar'})
+
+
class StreamingHttpResponseTests(TestCase):
def test_streaming_response(self):
r = StreamingHttpResponse(iter(['hello', 'world']))
View
6 tests/view_tests/generic_urls.py
@@ -56,3 +56,9 @@
(r'^shortcuts/render/dirs/$', 'render_with_dirs'),
(r'^shortcuts/render/current_app_conflict/$', 'render_view_with_current_app_conflict'),
)
+
+# json response
+urlpatterns += patterns('view_tests.views',
+ (r'^json/response/$', 'json_response_view'),
+)
+
View
24 tests/view_tests/tests/test_json.py
@@ -0,0 +1,24 @@
+# encoding: utf8
+from __future__ import unicode_literals, absolute_import
+
+import json
+
+from django.test import TestCase
+from django.test.utils import override_settings
+
+
+class JsonResponseTests(TestCase):
+ urls = 'view_tests.generic_urls'
+
+ @override_settings(DEFAULT_CHARSET='utf8')
+ def test_json_response(self):
+ response = self.client.get('/json/response/')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ response['content-type'], 'application/json')
+ self.assertEqual(json.loads(response.content.decode()), {
+ 'a': [1, 2, 3],
+ 'foo': {'bar': 'baz'},
+ 'timestamp': '2013-05-19T20:00:00',
+ 'value': '3.14',
+ })
View
14 tests/view_tests/views.py
@@ -1,11 +1,13 @@
from __future__ import unicode_literals
+import datetime
+import decimal
import os
import sys
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.urlresolvers import get_resolver
-from django.http import HttpResponse, HttpResponseRedirect
+from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render_to_response, render
from django.template import Context, RequestContext, TemplateDoesNotExist
from django.views.debug import technical_500_response, SafeExceptionReporterFilter
@@ -334,3 +336,13 @@ def multivalue_dict_key_error(request):
exc_info = sys.exc_info()
send_log(request, exc_info)
return technical_500_response(request, *exc_info)
+
+
+def json_response_view(request):
+ return JsonResponse({
+ 'a': [1, 2, 3],
+ 'foo': {'bar': 'baz'},
+ # Make sure datetime and Decimal objects would be serialized properly
+ 'timestamp': datetime.datetime(2013, 5, 19, 20),
+ 'value': decimal.Decimal('3.14'),
+ })

May as well put in the newline here.

@lukaszb
lukaszb added a note

I'm not sure what do you mean by that? New line at the end of the file? Or newline character inside response?

@timgraham Owner

Yes, add the newline at the EOF (I think it's a flake8 error not to have it?).

@lukaszb
lukaszb added a note

Hmm, I believe it's the other way round (flake8 warns about extra newline at the end of the file...)

@timgraham Owner

There should be 1 newline (the little icon that github displays shouldn't appear).

@lukaszb
lukaszb added a note

Ok, can do that. Just want to make 100% sure of this. Have just looked into some random files within Django (contrib/admin, core/urlresolvers) and there are no newlines at the end of the files (not a single one). And when I add that extra newline, flake8 warns me about this.

You'd still want me to add that newline?

@charettes Collaborator

@lukaszb which contrib/admin file are you talking about? Notice how contrib/admin/options.py doesn't have the missing new line icon.

Make sure not to add two newlines, just put your cursor right where the missing new line icon is and press ENTER once.

@timgraham Owner

If I pull your branch I get this error: "./tests/view_tests/views.py:348:7: W292 no newline at end of file"

That's different from adding a blank line at the of file which results in an error like this: "./tests/view_tests/generic_urls.py:64:1: W391 blank line at end of file"

@lukaszb
lukaszb added a note

Right... my bad, totally forgotten about my vim config related with newlines at the end of the file. Should be fixed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.