从另一个视图调用ViewSet方法

10
我有一个移动应用程序,使用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`

有没有其他的解决方案?
3个回答

17

经过一些研究,看起来在其他视图中重用请求可能会产生副作用,因此 DRF 不正式支持它。虽然这很 hacky,但如果你知道自己在做什么,这是一个解决方案。

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

5
注意事项:这似乎对于GET请求效果很好,但是对于POST请求,我会收到这个错误:django.http.request.RawPostDataException: You cannot access body after reading from request's data stream - decibyte
关于补丁调用,我们需要在URL中传递参数,例如/some/url/id,您怎么看? - Kashif Anwar

2
我有两种方法解决这个问题。假设我有一个视图A和一个视图集B,我想从A调用B.my_actionB.my_action处理特定的id,例如用户id、产品id等。
第一种方法:
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

在 AView.utils.py 中的代码调用 BViewSet.my_action

下面的request是通过 Web UI 调用 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调用中解耦,即:

在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

在Aview中的代码

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

1
在我的情况下,只需要这样做:
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。我想在你的示例中,我应该用 id 替换 user,用 my_id 替换 user_id - vpap
1
当然,首先,如果操作是列表或详细视图,您需要使用detail=True。我建议您重新排列URL,例如api/v1/my_action/id/<int:my_id>,所以是的,您需要将user替换为idùser替换为my_id,请查看DRF文档以获取更多信息https://www.django-rest-framework.org/api-guide/viewsets/。 - Manuel Carrero
还没有到那个地步。1. 如果有区别,我会从另一个视图的utils.py中调用所需的视图。然后,我执行MyViewSet(detail=True).my_action(request, pk=request.data['my_id']),成功调用了my_action,但是当我在操作中执行instance = self.get_object()时,我会收到错误AttributeError:'MyViewSet'对象没有属性'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() - vpap
据我理解,我的问题在于当我调用 MyViewSet.my_action() 时,我没有一个特定的实例。看起来我有一组对象。顺便说一下,basenameNone - vpap
我认为在调用MyViewSet时不需要传递detail=True,因此您可以调用MyViewSet().my_action(request, pk=request.data['my_id'])(或创建您类的实例)。您尝试过像这样定义您的操作吗:def my_action(self, request, *args, **kwargs)并且在kwargs中接收pk变量? - Manuel Carrero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接