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?

Asked By: Shameer Kashif

||

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.

Answered By: Dalvtor

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.

Answered By: Willem Van Onsem
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.