简体   繁体   中英

How to get data from ModelViewSet in DRF without calling an API call

I'm going to convert all my APIs into gRPC calls. At the moment I was able to transfer all the ViewSet into gRPC (sample code added end of this question). But ModelViewSet , it get an error like this.

Traceback (most recent call last):
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/venv/lib/python3.6/site-packages/grpc/_server.py", line 435, in _call_behavior
    response_or_iterator = behavior(argument, context)
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/servicers/tenant/main.py", line 15, in get_tenant
    data = ClientViewSet().list(request=original_request, )
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/common/lib/decorators.py", line 128, in wrapper_format_response
    final_data = call_func(func, self, request, transaction, exception, *args, **kwargs)
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/common/lib/decorators.py", line 99, in call_func
    return func(self, request, *args, **kwargs)
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/api_v1/viewsets.py", line 471, in list
    data = super().list(request, *args, **kwargs).data
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/venv/lib/python3.6/site-packages/rest_framework/mixins.py", line 38, in list
    queryset = self.filter_queryset(self.get_queryset())
  File "/home/wasdkiller/PycharmProjects/ocsa/dataengine-service/venv/lib/python3.6/site-packages/rest_framework/generics.py", line 158, in filter_queryset
    queryset = backend().filter_queryset(self.request, queryset, self)
AttributeError: 'ClientViewSet' object has no attribute 'request'

So my viewsets.py look like this ( format_response decorator convert it to Response object)

class ClientViewSet(viewsets.ModelViewSet):
    queryset = Client.objects.all()
    serializer_class = ser.ClientSerializer

    @format_response(exception=False)
    def list(self, request, *args, **kwargs):
        data = super().list(request, *args, **kwargs).data
        # data = {}
        return data, True, HTTPStatus.OK, 'data retrieve successfully'

When I call this as an API, it works perfectly. But I want to do the same thing without calling an API. Here how I was solving it,

from django.http import HttpRequest
from rest_framework.request import Request


# creating request object
django_request = HttpRequest()
django_request.method = 'GET'
drf_request = Request(django_request)

data = ClientViewSet().list(request=drf_request)
print(f'data: {data.data}')

The problem with the super() function in the ClientViewSet , but if I uncomment data = {} and comment out the calling super() function, it works both API and the above method. I go through inside the DRF code base where error occurred, when calling the API the self object has the request object and above method it doesn't exist.

In short, you need to call as_view() on the viewset and map the http verbs, and then call that function with a proper HttpRequest.

All views in django end up being functions, DRF is sugar on top of that. A "ViewSet" doesn't exist in the normal way, as a standalone class, after routing is complete.

django_request = HttpRequest()
django_request.method = 'GET'

my_view = ClientViewSet.as_view({'get': 'list', 'post':'create'})
data = my_view(request=django_request)
print(f'data: {data.data}')

If you want the detail routes ( users/37 , ...), then you need to create a new view function mapping to those viewset functions. This can't be the same view function because http get now needs to point to a different function on the viewset than in the list case. See routers.py source for whats going on and what is mapped where.

# map http 'get' to the 'retrive' function on the viewset
my_view = ClientViewSet.as_view({'get': 'retrieve', ...})

# pass in the kwarg the URL routing would normally extract
client_pk = 9329032 
data = my_view(request=django_request, pk=client_pk)

If you want to see what all the mappings for your viewset are, then you an print them out using this snippet:

router = SimpleRouter()
router.register("client", ClientViewSet, basename="client")
for url in router.urls:  # type: URLPattern
    print(f"{url.pattern} ==> {url.callback}")
    for verb, action in url.callback.actions.items():
        print(f"    {verb} -> {action}")

# output will be something like this
^client/$ ==> <function ClientViewSet at 0x11b91c280>
    get -> list
^client/(?P<pk>[^/.]+)/$ ==> <function ClientViewSet at 0x11b91c3a0>
    get -> retrieve
    put -> update
    patch -> partial_update
    delete -> destroy

The kwargs you pass to this view will depend on the settings in your ViewSet for things like lookup_url_kwarg , but in most cases they will be simple.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM