[Answered ]-How to call API view from another View in Django REST framework?

0👍

The problem was not in the code, it’s weird that I had only to re-run Django server and the code will be executed as expected, so B.views.py will be:


from rest_framework import views
from rest_framework.response import Response

from A.views import A

class B(views.APIView):

   def post(self, request, *args, **kwargs):

       http_response = A.as_view()(request._request)

       return Response(http_response.data)

UPDATE

it seems that HttpRequest AKA request._request can’t be used multiple times after it has been read by Django Rest Framework View parser class that will lead to call read method which will change the _request object’s property _read_started to be True, and after that if you tried to use request.data or request._request will raise the exception that shown above because the request.body is not accessible anymore due it counts on the value of _read_started property, also not to mention that the value of HttpRequest has already been converted into stream object and stored in the memory.

SOLUTION

There are multiple suggestions I found:

1- use Middleware to store the request.body to use it later in case of need.

2- use a custom parser class that store raw data as property in the request object before converting it into stream object.

Both of them I found is a little bit complicated, so I changed the way of calling another APIView from certain DRF View, as shown:


from rest_framework import views, status
from rest_framework.response import Response

from A.views import A
from C.views import C

class B(views.APIView):

   def post(self, request, *args, **kwargs):

       # Initialize a new instance of class view 'A'  
       a_view = A()

       # Calling HTTP 'POST' method with DRF request object.
       a_http_response = a_view.post(request)

       if a_http_response.status_code == status.status.HTTP_200_OK:

          # Initialize a new instance of class view 'C'  
          c_view = C()

          # Another calling HTTP 'POST' method with DRF request object.
          c_http_response = c_view.post(request)

          return(c_http_response.data)
       
       else:
          return Response(a_http_response.data)

1👍

I think isn’t the best practice in Django or DRF to call a view from another. If you want to access the business logic from View A by View B, I recommend you put your business logic in a function outside, in a way you can call that function on both Views A and B.

Said that, you can call APIView A from by APIView B by invoking the HTTP method post from the DRF APIView, such as A().post(request).

from rest_framework import views
from rest_framework.response import Response


class A(views.APIView):
   def post(self, request, *args, **kwargs):
       return Response({"message": "class A"})


class B(views.APIView):
    def post(self, request, *args, **kwargs):
        http_response = A().post(request)
        return Response({"message": http_response.data})

0👍

You can use the requests library to send a post to your endpoint:

A.views.py

class A(views.APIView):
    def post(self, request, *args, **kwargs):
        return Response({"message": "class A"})

B.views.py

import requests


class B(views.APIView):
    def get(self, request, *args, **kwargs):
        
        r = requests.post("http://localhost:8000/api/a")
        r_data = r.json()
        message = r_data['message']

        data = {
            'status_code': r.status_code,
            'headers': r.headers,
            'message_from_a': message
        }
        return Response(data)

Obs: Override get on B so you can see the message on browsable API.

👤Niko

Leave a comment