How to send HTTP response in Json format in Generic Class Views [Django]
Question:
I have sub-classed Generic DetailView class in views.py
and trying to figure out a way to return data in JSON format based on an argument received in the url. Here’s what I have tried doing…
# views.py
from django.views.generic import DetailView
from django.http import JsonResponse
class ExtendedView(DetailView):
context_object_name = 'post'
model = StorageModel
template_name='posts.html'
def get_context_data(self, **kwargs):
data = super(HacksViewPost, self).get_context_data(**kwargs)
if bool(self.request.GET):
data__ = JsonForm(request.GET)
if data__.is_valid():
json = data__.cleaned_data['json']
if json == 'true':
return JsonResponse({'data': 'data'})
return data
But this gave me TypeError
as it should be:
TypeError at /category/extended-slug/
context must be a dict rather than JsonResponse.
The url that activates the ExtendedView
class is:
/category/extended-slug?json=true
So, the question is how could i send data in JSON Format from a Generic View Class and are there any better ways of acheiving this?
Answers:
You can’t return a JsonResponse
inside the get_context_data
method. The get_context_data
method allows you to send extra information (context) to the template and is expected to return a dict
, not a JsonResponse
.
If you want to return a JsonResponse
, do that in the get
or post
method of your class.
I think you patch it at the wrong level. The get_context_data
is used by the get
function to render it. As a result, the get_context_data
object has no control about what is done with the result, in order to construct a server response,
You can however patch the get(..)
function like:
class ExtendedView(DetailView):
"""A base view for displaying a single object."""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
data = self.get_context_data(object=self.object)
if self.request.GET:
data__ = JsonForm(request.GET)
if data__.is_valid():
json = data__.cleaned_data['json']
if json == 'true':
return JsonResponse({'data': data})
return self.render_to_response(data)
The same holds for post
, put
, and other requests.
If we take a look at the DetailView
source code we see:
class BaseDetailView(SingleObjectMixin, View):
"""A base view for displaying a single object."""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
Hence the get(..)
calls the get_context_data(..)
function. But it does not immediately returns the result, it wraps it into a rendered response.
I have sub-classed Generic DetailView class in views.py
and trying to figure out a way to return data in JSON format based on an argument received in the url. Here’s what I have tried doing…
# views.py
from django.views.generic import DetailView
from django.http import JsonResponse
class ExtendedView(DetailView):
context_object_name = 'post'
model = StorageModel
template_name='posts.html'
def get_context_data(self, **kwargs):
data = super(HacksViewPost, self).get_context_data(**kwargs)
if bool(self.request.GET):
data__ = JsonForm(request.GET)
if data__.is_valid():
json = data__.cleaned_data['json']
if json == 'true':
return JsonResponse({'data': 'data'})
return data
But this gave me TypeError
as it should be:
TypeError at /category/extended-slug/
context must be a dict rather than JsonResponse.
The url that activates the ExtendedView
class is:
/category/extended-slug?json=true
So, the question is how could i send data in JSON Format from a Generic View Class and are there any better ways of acheiving this?
You can’t return a JsonResponse
inside the get_context_data
method. The get_context_data
method allows you to send extra information (context) to the template and is expected to return a dict
, not a JsonResponse
.
If you want to return a JsonResponse
, do that in the get
or post
method of your class.
I think you patch it at the wrong level. The get_context_data
is used by the get
function to render it. As a result, the get_context_data
object has no control about what is done with the result, in order to construct a server response,
You can however patch the get(..)
function like:
class ExtendedView(DetailView):
"""A base view for displaying a single object."""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
data = self.get_context_data(object=self.object)
if self.request.GET:
data__ = JsonForm(request.GET)
if data__.is_valid():
json = data__.cleaned_data['json']
if json == 'true':
return JsonResponse({'data': data})
return self.render_to_response(data)
The same holds for post
, put
, and other requests.
If we take a look at the DetailView
source code we see:
class BaseDetailView(SingleObjectMixin, View): """A base view for displaying a single object.""" def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) return self.render_to_response(context)
Hence the get(..)
calls the get_context_data(..)
function. But it does not immediately returns the result, it wraps it into a rendered response.