如何在Django REST框架中从另一个View调用API View?

bz4sfanl  于 2023-07-01  发布在  Go
关注(0)|答案(3)|浏览(171)

我尝试从另一个APIView调用APIView,代码类似于:

A.views.py

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"})

B.views.py

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 = <call_A_APIView>

       return Response({"message": http_response})

我已经尝试了下面的代码行:

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

但提出以下例外:
从请求的数据流中阅读后无法访问正文
你知道该怎么做吗?

2exbekwf

2exbekwf1#

我认为在Django或DRF中调用另一个视图不是最好的做法。如果你想通过视图B从视图A访问业务逻辑,我建议你把业务逻辑放在一个外部函数中,这样你就可以在视图A和B上调用这个函数。
也就是说,你可以通过从DRF APIView调用HTTP方法 post,比如A().post(request),由APIView B调用APIView A。

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})
6pp0gazn

6pp0gazn2#

您可以使用requests库向端点发送post
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:在B上覆盖get,以便您可以在可浏览API上看到消息。

oo7oh9g9

oo7oh9g93#

问题不在代码中,奇怪的是,我只需要重新运行Django服务器,代码就会按预期执行,所以B.views.py将是:

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)

更新

似乎HttpRequest AKA request._request在被Django Rest Framework View解析器类读取后不能多次使用,这将导致调用read方法,该方法将_request对象的属性_read_started更改为True,之后,如果您尝试使用request.datarequest._request将引发上面显示的异常,因为request.body不再可访问,因为它依赖于_read_started属性的值,更不用说HttpRequest的值已经被转换为stream对象并存储在内存中。

解决方案

我发现了很多建议:
1-使用Middleware来存储request.body,以便以后需要时使用。
2-使用自定义解析器类,在将原始数据转换为stream对象之前,将其存储为request对象中的属性。
我发现这两个都有点复杂,所以我改变了从某个DRF View调用另一个APIView的方式,如图所示:

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)

相关问题