Class based generic views that render JSON data.
The django.views.generic
package has several generic class based views
that render HTML for a model or a set of applications and provide an API
to use those views and modify querysets and context data. However, Django
recommends that to specify the output of JSON data, you use a mixin or
another approach to render_to_response
the JSON.
Many AJAX applications, however, have the need for quick views like the
ones specified the in the generic
package to either serialize a model
or to provide other context data in the View logic. These generic views
allow that, and correctly render JSON data with the correct HTTP Headers.
More documentation can be found at Read the Docs: Django Generic JSON Views Documentation
The easiest way to install this library is to use pip
as follows:
$ pip install django-generic-json-views
Alternatively you can download the source code and run the following:
$ python setup.py install
You can import from the json_views.views
module.
Otherwise copy the views.py file to a utils directory in your Django Project and import it correctly from there.
There are several provided classes that work right off the bat:
JSONDataView
JSONDetailView
JSONListView
PaginatedJSONListView
JSONFormView
These classes all provide the following functionality:
- Render the output of
get_context_data
as a JSON object - Serialize Django Models and Querysets with a Lazy Encoder
- Correctly add the application/json HTTP header
- Remove duplicate objects and middleware context built into Django
The most simple example is the JSONDataView
which will allow you to output generic JSON data as needed:
from datetime import datetime
from json_views.views import JSONDataView
class JSONTime(JSONDataView):
def get_context_data(self, **kwargs):
context = super(JSONTime, self).get_context_data(**kwargs)
context['current_time'] = datetime.now().strftime("%c")
return context
In order to render the detail view of a model as a JSON serialization,
use the JSONDetailView
-- note that this view can be used for both
serialization and deserialization as noted in the section below.
from json_views.views import JSONDetailView
class CustomerJSON(JSONDetailView):
model = Customer
Many JSON views are centered around lists, hence the JSONListView
and
the PaginatedJSONListView
.
from json_views.views import JSONListView
class ProductsJSON(JSONListView):
model = Product
The PaginatedJSONListView
looks up the special parameter page=n
to
decide which part of the list to return. It also removes the Django paginator
object and returns the following data:
{
'pages': Number,
'count': Number,
'is_paginated': Bool,
'per_page': Number,
'current': Number,
'object_list': List
}
An example of using this view is:
from json_views.views import PaginatedJSONListView
class PaginatedProducts(PaginatedJSONListView):
paginate_by = 10
count_query = 'count'
count_only = False
def get_querset(self):
return Product.objects.filter(instores=True)
count_query
specifies the name of the property to pass in as kwargs in
a GET request that will force the view to only return the total number
of expected items, and not fetch them from the db. E.g. if you use
/?count=true
in your url, the view will respond only with the total number.
Finally the JSONFormView accepts incoming JSON data to populate a form rather than waiting for HTTP POST data, this is how you would do AJAX login forms. Note that GET requests are not allowed.
from json_views.views import JSONFormView
class LoginForm(JSONFormView):
form = LoginForm
In the form view, a parameter success
is passed back, as either True or
False. This can be customized by overriding get_context_data
.
The lazy encoder attempts to serialze the model into a dictionary or other compatible JSON-Python type, and does so in a recursive fashion, continuing to serialize objects, the default serialization is this:
- If the object is an iterable- construct a list
- If the object extends
ModelBase
attempt to call aserialize
method - Utilize
force_unicode
to force the object to unicode data.
Therefore if your Models
provide a serialize
method that returns a
dictionary, you can very easily control what fields and what format the
model is serialized to.
Additionally you can specify a @classmethod
deserialize
that accepts
as input a dictionary and returns an instance of the Model
.
Here is an example of the serialization of a UserProfile, as an extension
of the django.contrib.auth
package.
class UserProfile(models.Model):
user = models.OneToOneField( 'django.contrib.auth.User', editable=False, related_name='profile' )
bio = models.CharField( max_length=255, null=True, blank=True, default=None )
birth = models.DateField( )
def serialize(self):
return {
'username': self.user.username,
'email': self.user.email,
'name': " ".join((self.user.first_name, self.user.last_name)),
'birthday': self.birth.strftime("%d %M, %Y"),
}
@classmethod
def deserialize(klass, data):
username = data.get('username', None)
if username:
try:
return klass.objects.get(username=username)
except klass.DoesNotExist:
pass
return klass(**data)
A couple of notes on pushing this software to PyPi:
- Version Bump
- Change the version in
__init__
.py - Change the version in tests
- Change the download URL in setup.py
- Change the version in
- Tag the release
git tag -a v0.x.x
- Push to Github
- Register
python setup.py register -r pypi
- Upload
python setup.py sdist upload -r pypi
Thanks to @philippbosch for helping provide Python 3 support with later versions of Django.
Thanks to @freeworlder who provided Django 1.10 compatibility.