Making a custom JSON response in Django

Hey guys, welcome to the first of many coding posts. Today I’m going to talk a bit about the JSONField() attribute, and making custom JSON responses with the Django REST framework. 

The Task

So, I guess I should probably give a little background about this project first. Working with Tokyo University, my client and I are building an app that tracks a person’s movement from place to place, and asks different questions in a survey. I am tasked with building the API.

 

The JSON POST data will of this format: [{"survey_id":"568","bus_route":"234","quest1":0,"quest2":1,"quest2_text":null,"quest3":2,"quest3_text":null,"date_reg":1547730550767,"sent_flag":0}]

Since the data could change quite frequently, I decided that Django’s JSONField() would be the best field type to use, along with a separate survey_id field that ties different reports to a single survey using a ForeignKey.

Django has a simple way to do this with the JSONField() attribute.

 

The most difficult part was figuring out how to comply with my client’s requirement regarding the JSON response on POST.

from import_export import resources
from import_export.admin import ExportMixin

class UserInfoResource(resources.ModelResource):
    class Meta:
        model = UserInfo

 

class UserInfoAdmin(ExportMixin, admin.ModelAdmin):

    . . . 

 

The expected JSON response is:  {status: OK, report: , surveyid: 568}

How I made a custom JSON Response

 

There are several different methods one can utilize to make a custom JSON response with Django. I won’t go through every method, just the one that made sense for my case.

 

First, here’s an overview of the code

def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

This is the source code for the create function in Django REST framework, which you can view directly here

Below, you can see how I modified it in my own program. I basically kept everything the same, but added the JsonResponse function that Django has.

def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
response = JsonResponse({"status": "OK", "report": "", "surveyid":
serializer.data["survey_id"]})
response.status_code = status.HTTP_201_CREATED
return response

Reading through the code line by line:

  1. First, the serializer variable is set do include the data so that it’s easy to access.
  2. Then the data is checked to see if it’s valid. For example, if a field in the Model requires an integer, it’ll throw an error, thus preventing the data from being POSTed to the database.
  3. If the data is valid, it is posted.
  4. The response variable is where I started to write in my code. Instead of returning a normal Response, this tells Django that you want to return a custom JsonResponse.
  5. After that, I added the status code (HTTP_201_created).
  6. The response is returned.

Now, when there is a POST request sent to the server for that particular model, the following response is sent:

 

{status: OK, report: , surveyid: 568}

This can be used for any request

If you need to make a custom JSON response for POST, GET, DELETE, or PUT, this can all be done through the ModelViewSet, using the methods that the REST framework uses in liu of the above (Create, RetrieveUpdateDestroy

For the time being, in my project I had basically copy/pasted my code into the few model viewsets that are used for POST requests. However, this is not maintainable in the long run and I plan to replace the repeated code with a custom mixin.

“Keep it DRY when you can, but don’t forget to get shit done!”