Always return JSON from your Django view.
Project description
===============
django-jsonview
===============
.. image:: https://travis-ci.org/jsocol/django-jsonview.png?branch=master
:target: https://travis-ci.org/jsocol/django-jsonview
**django-jsonview** is a simple decorator that translates Python objects
to JSON and makes sure your view will always return JSON.
I've copied and pasted this so often I decided I just wanted to put it
in a package.
Installation
============
Just install with ``pip``::
pip install django-jsonview
No need to add to ``INSTALLED_APPS`` or anything.
Usage
=====
Just import the decorator, use, and return a JSON-serializable object::
from jsonview.decorators import json_view
@json_view
def my_view(request):
return {
'foo': 'bar',
}
Content Types
-------------
If you need to return a content type other than the standard
``application/json``, you can specify that in the decorator with the
``content_type`` argument, for example::
from jsonview.decorators import json_view
@json_view(content_type='application/vnd.github+json')
def myview(request):
return {'foo': 'bar'}
The response will have the appropriate content type header.
Return Values
-------------
The default case is to serialize your return value and respond with HTTP
200 and a Content-Type of ``application/json``.
The ``@json_view`` decorator will handle many exceptions and other
cases, including:
* ``Http404``
* ``PermissionDenied``
* ``HttpResponseNotAllowed`` (e.g. ``require_GET``, ``require_POST``)
* ``jsonview.exceptions.BadRequest`` (see below)
* Any other exception (logged to ``django.request``).
Any of these exceptions will return the correct status code (i.e., 404,
403, 405, 400, 500) a Content-Type of ``application/json``, and a
response body that looks like::
json.dumps({
'error': STATUS_CODE,
'message': str(exception),
})
.. note::
As of v0.4, application exceptions do **not** behave this way if
``DEBUG = False``. When ``DEBUG = False``, the ``message`` value is
always ``An error occurred``. When ``DEBUG = True``, the exception
message is sent back.
``BadRequest``
--------------
HTTP does not have a great status code for "you submitted a form that
didn't validate," and so Django doesn't support it very well. Most
examples just return 200 OK.
Normally, this is fine. But if you're submitting a form via Ajax, it's
nice to have a distinct status for "OK" and "Nope." The HTTP 400 Bad
Request response is the fallback for issues with a request
not-otherwise-specified, so let's do that.
To cause ``@json_view`` to return a 400, just raise a
``jsonview.exceptions.BadRequest`` with whatever appropriate error
message.
Exceptions
----------
If your view raises an exception, ``@json_view`` will catch the
exception, log it to the normal ``django.request`` logger_, and return a
JSON response with a status of 500 and a body that looks like the
exceptions in the `Return Values`_ section.
.. note::
Because the ``@json_view`` decorator handles the exception instead of
propagating it, any exception middleware will **not** be called, and
any response middleware **will** be called.
Status Codes
------------
If you need to return a different HTTP status code, just return two
values instead of one. The first is your serializable object, the second
is the integer status code::
@json_view
def myview(request):
if not request.user.is_subscribed():
# Send a 402 Payment Required status.
return {'subscribed': False}, 402
# Send a 200 OK.
return {'subscribed': True}
Extra Headers
-------------
You can add custom headers to the response by returning a tuple of three
values: an object, a status code, and a dictionary of headers.
::
@json_view
def myview(request):
return {}, 200, {'X-Server': 'myserver'}
Custom header values may be overwritten by response middleware.
Raw Return Values
-----------------
To make it possible to cache JSON responses as strings (and because they
aren't JSON serializable anyway) if you return an ``HttpResponse``
object (or subclass) it will be passed through unchanged, e.g.::
from django import http
from jsonview.decorators import JSON
@json_view
def caching_view(request):
kached = cache.get('cache-key')
if kached:
return http.HttpResponse(kached, content_type=JSON)
# Assuming something else populates this cache.
return {'complicated': 'object'}
.. note::
``@require_POST`` and the other HTTP method decorators work by
*returning* a response, rather than *raising*, an an exception, so
``HttpResponseNotAllowed`` is handled specially.
Alternative JSON Implementations
================================
There is a healthy collection of JSON parsing and generating libraries
out there. By default, it will use the old standby, the stdlib ``json``
module. But, if you'd rather use ujson_, or cjson_ or yajl_, you should
go for it. Just add this to your Django settings::
JSON_MODULE = 'ujson'
Anything, as long as it's a module that has ``.loads()`` and ``.dumps()``
methods.
Configuring JSON Output
-----------------------
.. versionadded:: 0.5
Additional keyword arguments can be passed to ``json.dumps()`` via the
``JSON_OPTIONS = {}`` Django setting. For example, to pretty-print JSON
output::
JSON_OPTIONS = {
'indent': 4,
}
Or to compactify it::
JSON_OPTIONS = {
'separators': (',', ':'),
}
To use Django's ``DjangoJSONEncoder``, add a ``cls``::
JSON_OPTIONS = {
'cls': 'django.core.serializers.json.DjangoJSONEncoder',
}
``JSON_OPTIONS['cls']`` may be a dotted string or a ``JSONEncoder``
class.
.. versionchanged:: 0.6
Through version 0.5, ``@json_view`` uses Django's ``DjangoJSONEncoder``
class by default. If the JSON module you're using does not support the
``cls`` kwarg, use the following setting to not set a serializer::
JSON_USE_DJANGO_SERIALIZER = False
.. note::
Without the Django serializer, ``datetime.datetime`` objects will not
be automatically serializable.
.. warning::
This behavior will change in 0.6.
Atomic Requests
===============
Because ``@json_view`` catches exceptions, the normal Django setting
``ATOMIC_REQUESTS`` does not correctly cause a rollback. This can be
worked around by explicitly setting ``@transaction.atomic`` *below* the
``@json_view`` decorator, e.g.::
@json_view
@transaction.atomic
def my_func(request):
# ...
Contributing
============
`Pull requests`_ and issues_ welcome! I ask two simple things:
* Tests, including the new ones you added, must pass. (See below.)
* The ``flake8`` tool should not return any issues.
Running Tests
-------------
To run the tests, you probably want to create a virtualenv_, then
install Django and Mock with ``pip``::
pip install Django==${DJANGO_VERSION} mock==1.0.1
Then run the tests with::
./run.sh test
.. _logger:
https://docs.djangoproject.com/en/dev/topics/logging/#django-request
.. _Pull requests: https://github.com/jsocol/django-jsonview/pulls
.. _issues: https://github.com/jsocol/django-jsonview/issues
.. _virtualenv: http://www.virtualenv.org/
.. _ujson: https://pypi-hypernode.com/pypi/ujson
.. _cjson: https://pypi-hypernode.com/pypi/python-cjson
.. _yajl: https://pypi-hypernode.com/pypi/yajl
django-jsonview
===============
.. image:: https://travis-ci.org/jsocol/django-jsonview.png?branch=master
:target: https://travis-ci.org/jsocol/django-jsonview
**django-jsonview** is a simple decorator that translates Python objects
to JSON and makes sure your view will always return JSON.
I've copied and pasted this so often I decided I just wanted to put it
in a package.
Installation
============
Just install with ``pip``::
pip install django-jsonview
No need to add to ``INSTALLED_APPS`` or anything.
Usage
=====
Just import the decorator, use, and return a JSON-serializable object::
from jsonview.decorators import json_view
@json_view
def my_view(request):
return {
'foo': 'bar',
}
Content Types
-------------
If you need to return a content type other than the standard
``application/json``, you can specify that in the decorator with the
``content_type`` argument, for example::
from jsonview.decorators import json_view
@json_view(content_type='application/vnd.github+json')
def myview(request):
return {'foo': 'bar'}
The response will have the appropriate content type header.
Return Values
-------------
The default case is to serialize your return value and respond with HTTP
200 and a Content-Type of ``application/json``.
The ``@json_view`` decorator will handle many exceptions and other
cases, including:
* ``Http404``
* ``PermissionDenied``
* ``HttpResponseNotAllowed`` (e.g. ``require_GET``, ``require_POST``)
* ``jsonview.exceptions.BadRequest`` (see below)
* Any other exception (logged to ``django.request``).
Any of these exceptions will return the correct status code (i.e., 404,
403, 405, 400, 500) a Content-Type of ``application/json``, and a
response body that looks like::
json.dumps({
'error': STATUS_CODE,
'message': str(exception),
})
.. note::
As of v0.4, application exceptions do **not** behave this way if
``DEBUG = False``. When ``DEBUG = False``, the ``message`` value is
always ``An error occurred``. When ``DEBUG = True``, the exception
message is sent back.
``BadRequest``
--------------
HTTP does not have a great status code for "you submitted a form that
didn't validate," and so Django doesn't support it very well. Most
examples just return 200 OK.
Normally, this is fine. But if you're submitting a form via Ajax, it's
nice to have a distinct status for "OK" and "Nope." The HTTP 400 Bad
Request response is the fallback for issues with a request
not-otherwise-specified, so let's do that.
To cause ``@json_view`` to return a 400, just raise a
``jsonview.exceptions.BadRequest`` with whatever appropriate error
message.
Exceptions
----------
If your view raises an exception, ``@json_view`` will catch the
exception, log it to the normal ``django.request`` logger_, and return a
JSON response with a status of 500 and a body that looks like the
exceptions in the `Return Values`_ section.
.. note::
Because the ``@json_view`` decorator handles the exception instead of
propagating it, any exception middleware will **not** be called, and
any response middleware **will** be called.
Status Codes
------------
If you need to return a different HTTP status code, just return two
values instead of one. The first is your serializable object, the second
is the integer status code::
@json_view
def myview(request):
if not request.user.is_subscribed():
# Send a 402 Payment Required status.
return {'subscribed': False}, 402
# Send a 200 OK.
return {'subscribed': True}
Extra Headers
-------------
You can add custom headers to the response by returning a tuple of three
values: an object, a status code, and a dictionary of headers.
::
@json_view
def myview(request):
return {}, 200, {'X-Server': 'myserver'}
Custom header values may be overwritten by response middleware.
Raw Return Values
-----------------
To make it possible to cache JSON responses as strings (and because they
aren't JSON serializable anyway) if you return an ``HttpResponse``
object (or subclass) it will be passed through unchanged, e.g.::
from django import http
from jsonview.decorators import JSON
@json_view
def caching_view(request):
kached = cache.get('cache-key')
if kached:
return http.HttpResponse(kached, content_type=JSON)
# Assuming something else populates this cache.
return {'complicated': 'object'}
.. note::
``@require_POST`` and the other HTTP method decorators work by
*returning* a response, rather than *raising*, an an exception, so
``HttpResponseNotAllowed`` is handled specially.
Alternative JSON Implementations
================================
There is a healthy collection of JSON parsing and generating libraries
out there. By default, it will use the old standby, the stdlib ``json``
module. But, if you'd rather use ujson_, or cjson_ or yajl_, you should
go for it. Just add this to your Django settings::
JSON_MODULE = 'ujson'
Anything, as long as it's a module that has ``.loads()`` and ``.dumps()``
methods.
Configuring JSON Output
-----------------------
.. versionadded:: 0.5
Additional keyword arguments can be passed to ``json.dumps()`` via the
``JSON_OPTIONS = {}`` Django setting. For example, to pretty-print JSON
output::
JSON_OPTIONS = {
'indent': 4,
}
Or to compactify it::
JSON_OPTIONS = {
'separators': (',', ':'),
}
To use Django's ``DjangoJSONEncoder``, add a ``cls``::
JSON_OPTIONS = {
'cls': 'django.core.serializers.json.DjangoJSONEncoder',
}
``JSON_OPTIONS['cls']`` may be a dotted string or a ``JSONEncoder``
class.
.. versionchanged:: 0.6
Through version 0.5, ``@json_view`` uses Django's ``DjangoJSONEncoder``
class by default. If the JSON module you're using does not support the
``cls`` kwarg, use the following setting to not set a serializer::
JSON_USE_DJANGO_SERIALIZER = False
.. note::
Without the Django serializer, ``datetime.datetime`` objects will not
be automatically serializable.
.. warning::
This behavior will change in 0.6.
Atomic Requests
===============
Because ``@json_view`` catches exceptions, the normal Django setting
``ATOMIC_REQUESTS`` does not correctly cause a rollback. This can be
worked around by explicitly setting ``@transaction.atomic`` *below* the
``@json_view`` decorator, e.g.::
@json_view
@transaction.atomic
def my_func(request):
# ...
Contributing
============
`Pull requests`_ and issues_ welcome! I ask two simple things:
* Tests, including the new ones you added, must pass. (See below.)
* The ``flake8`` tool should not return any issues.
Running Tests
-------------
To run the tests, you probably want to create a virtualenv_, then
install Django and Mock with ``pip``::
pip install Django==${DJANGO_VERSION} mock==1.0.1
Then run the tests with::
./run.sh test
.. _logger:
https://docs.djangoproject.com/en/dev/topics/logging/#django-request
.. _Pull requests: https://github.com/jsocol/django-jsonview/pulls
.. _issues: https://github.com/jsocol/django-jsonview/issues
.. _virtualenv: http://www.virtualenv.org/
.. _ujson: https://pypi-hypernode.com/pypi/ujson
.. _cjson: https://pypi-hypernode.com/pypi/python-cjson
.. _yajl: https://pypi-hypernode.com/pypi/yajl
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
django-jsonview-0.5.0.tar.gz
(11.6 kB
view details)
Built Distribution
File details
Details for the file django-jsonview-0.5.0.tar.gz
.
File metadata
- Download URL: django-jsonview-0.5.0.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d1544e97dd5e625a77f9539002bcdec3e12c673bdadd491271782ef29016470a |
|
MD5 | 060972da1e1551e1931b3592be57bab4 |
|
BLAKE2b-256 | 27fe71c058f1b55618b42e64a3b38cc482b32d9589af5b2bced1547e120d2097 |
File details
Details for the file django_jsonview-0.5.0-py2.py3-none-any.whl
.
File metadata
- Download URL: django_jsonview-0.5.0-py2.py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 58cc5d5d992ae3fe36084c2eb103e02ec5fbb5470736df17f96b5c3bdc7c8138 |
|
MD5 | 794c19c4459cbfb848a064a39c35988c |
|
BLAKE2b-256 | d53ee0f7d4602ea6f648e8a5f3c80d8733d105de774c2a67dc621f3b941a8f03 |