【问题标题】:Call ViewSet method from another view从另一个视图调用 ViewSet 方法
【发布时间】:2018-12-11 11:22:43
【问题描述】:

我有一个带有 Django REST 框架 API 的移动应用程序,并且我有很多 ModelViewSet 可以调用来检索数据。我有性能问题,因为我需要在用户登录后调用很多路由,并且我想保留 REST 逻辑,但也在登录后返回登录响应中的所有视图集内容(保留它们的过滤器)。

是否可以从另一个视图(视图集或 APIView)调用 ModelViewset list()?

我在 SO 上找到的唯一答案是做这样的事情

class ContentGenerator(APIView):

    def get(self, request, format=None):
         data = MyModelViewSet.as_view({'get': 'list'})(request).data

         return Response({'data': data})

但不支持

Exception Value: The `request` argument must be an instance of `django.http.HttpRequest`, not `rest_framework.request.Request`

还有其他解决方案吗?

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    经过一番研究,似乎在其他视图中重用请求可能会有副作用,因此 DRF 并未正式支持。这很 hacky,但如果你知道自己在做什么,这就是一个解决方案。

    data = MyModelViewSet.as_view({'get': 'list'})(request._request).data
    

    【讨论】:

    • 注意事项:这似乎适用于 GET 请求,但对于 POST,我收到此错误:django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
    【解决方案2】:

    就我而言,这样做就足够了:

    data = MyClassViewSet()
    for i in data.queryset:
       # Then you can access to any field of your model like this:
       i.id
       i.name
       ...
       # And do what you need
    

    但显然这只适用于{'get':'list'}

    如果方法是额外的操作(例如:products_filtered_by_user),您可以这样做:

    data = MyClassViewSet()
    data.products_filtered_by_user(request,user=kwargs['user_id'])
    if data.code == 200:
        response_data = data.response_data
    

    【讨论】:

    • 你能解释一下如何调用额外的动作吗?对于我的额外操作,我有这个装饰器@action(detail=True, methods=['post']),如果我从 Web UI 调用它,则 URL 是 http://.../my_id/my_action。我想在您的示例中,我应该将 user 替换为 iduser_id 替换为 my_id
    • 当然,首先,如果操作是列表或详细视图,则需要使用detail=True。我建议你重新排序你的网址,比如api/v1/my_action/id/<int:my_id>,所以是的,你需要用id替换user,用my_id替换ùser,查看drf文档以获取更多信息django-rest-framework.org/api-guide/viewsets
    • 还没有。 1. 我从另一个视图的utils.py 调用所需的视图,如果它有不同的话。然后,我执行MyViewSet(detail=True).my_action(request, pk=request.data['my_id']),它成功调用my_action,但是当我在操作instance = self.get_object() 中执行操作时,我收到错误AttributeError: 'MyViewSet' object has no attribute 'kwargs'MyViewSet 同时具有 queryset = My.objects.all()serializer_class = MySerialzer。最后,@action(detail=True, methods=['post'] def my_action(self, request, pk=None): instance = self.get_object()
    • 据我了解,我的问题是当我调用MyViewSet.my_action() 时没有特定的实例。我有一个看起来的对象集合。顺便说一句,basenameNone
    • 我认为你在调用MyViewSet 时不需要传递detail=True,因此你可以调用它MyViewSet().my_action(request,pk=request.data['my_id'])(或创建你的类的实例)。您是否尝试过像def my_action(self, request, *args, **kwargs) 这样定义您的操作并在您的kwargs 中接收pk 变量?
    【解决方案3】:

    我通过两种方式解决了这个问题。假设我有一个view A 和一个viewset B,并且我想从A 调用B.my_actionB.my_action 处理特定的 id,例如用户 ID、产品 ID 等。

    第一种方式

    code w/in BViewSet

    @action(detail=True, methods=['post'])
    def my_action(self, request, pk=None):
     try:
       # works when you call from web UI, through router
       # automatically you have one specific view corresponding
       # to specific id e.g. user id, product id etc
       instance = self.get_object()
     except AttributeError as ae:
       # here you don't have a specific view instantiated
       # and you have many ids e.g. user ids, product ids etc
       # pick one id
       instance = MyModelObject.objects.get(id=pk)
    
     # rest of code as is, no change
    

    code w/in e.g. AView.utils.py that calls BViewSet.my_action

    request 下面是调用AView 时传递给AView 的请求 通过网络用户界面。

    from django.http import HttpRequest
    
    data = BViewSet(detail=True)
    new_request = HttpRequest()
    new_request.data = any_data_I_need_to_pass # e.g. request.data
    
    ret = data.my_action(new_request, pk=request.data['id'])
    return ret
    

    第二种方式

    我将my_action 功能与my_action 调用分离,即

    code w/in BViewSet

    @action(detail=True, methods=['post'])
    def my_action(self, request, pk=None):
      instance = self.get_object()
      ret = my_action_server(instance, request.data)
      return Response(data=ret)
    
    def my_action_server(instance, data):
       # all code comes here
       return result
    

    code w/in Aview

    # import my_action_server as needed
    # call my_action_server
    # AView should provide everything needed
    

    【讨论】:

      猜你喜欢
      • 2013-08-10
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 2011-12-11
      • 2021-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多