Call a viewset in another viewset with POST method

I want to call a function of a viewset in another viewset, with POST method.
I succeed this with GET :

params = {"number" : 44}
zm = ZoneViewSet.as_view({"get": "zm"})(request._request, **params).render().content

but, I can’t with POST, I don’t known how to do that :(, I’ve this error “GET method not allowed” :

params = {
    "target": datetime.now().strftime(self.format_date),
    "value": 9,
    "origin": "auto",
    "zm": level_zone.get("zm",0),
}
post_zm = AccessViewSet.as_view({"post": "add"})(request._request, **params).render().content

Here the “initial” POST function. I remove all unnecessary lines :

@action(detail=True, methods=["POST"])
    def add(self, request):
        try:
            data = request.data
            serializer = AccessLevelSerializer(data=data)
            response = self.RESPONSE_ACCESS_LEVEL

            if serializer.is_valid():
                serializer.save()
                response["request_params"] = serializer.data
                response["result"] = {"code_http": status.HTTP_201_CREATED}
                return Response(response, status=status.HTTP_201_CREATED)
            else:
                response["message_error"] = serializer.errors

            response["request_params"] = serializer.data
        except Exception as e:
            message = str(e).lower()
            response["message_error"] = message
            response["result"] = {"code_http": status.HTTP_409_CONFLICT}
            response["request_params"] = request.data
        finally:
            return Response(response, status=status.HTTP_201_CREATED)

and the viewset must reuse the POST method below :

@action(detail=True, methods=["POST"])
    def add(self, request, departement=None) -> Response:
        try:
            response = {"result": []}
            if data := self.level(request=request, departement=departement):
                levels_zones = self.response_to_json(data)
    
            for level_zone in levels_zones:
                params = {
                    "target_date": datetime.now().strftime(self.format_date),
                    "value": 9,
                    "origin": "auto",
                    "zm": level_zone.get("zm",0),
                }
                post_accesslevel = AccessViewSet.as_view({"post": "add"})(request._request, **params).render().content
    
        except ValueError as e:
            response = {"result": {"message_error": status.HTTP_406_NOT_ACCEPTABLE}}
        except Exception as e:
            response = {"result": {"message_error": status.HTTP_503_SERVICE_UNAVAILABLE}}
        finally:
            return Response(response)

In the urls.py :

access_level_add = access.AccessViewSet.as_view({"post": "add"})

Can you help me please ?

Thanks
F.

I think that what you actually want to do, is to define a function, that is responsible for this logic.
Let’s say:

def add_user_profile(some_data, other_data):
  # do something if data is valid
  # or raise an exception if not.

Then use this function on your viewset passing the data, from the serializer. Then you can reuse this function on the other viewset or anywhere.

Hi,
Thank you to anwser me, but I don’t really understand. Have you an example please ?

And, I don’t understand, either, why the GET method like that :

params = {"zm_number" : 44}
zm = ZmViewSet.as_view({"get": "zm"})(request._request, **params).render().content

is working, and not the POST.

I also try this :

post_accesslevel = AccessViewSet()
post_accesslevel.add(params) # add() is the POST function in the initial viewset

But I’ve this error :
"add(), except Exception : 'dict' object has no attribute 'data'"
Error line in the initial viewset : data = request.data

and this :

params = {
    "target_date": datetime.now().strftime(self.format_date),
    "value": 9,
    "origin": "auto",
    "zm":level_value,
}
post_accesslevel = AccessViewSet()
post_accesslevel.add(request)

In fact, the request object is from a GET method. The AccessViewSet need a POST method. I think I can’t inject it in the post_accesslevel.add().

And how can I pass the data (params dict) ?
Thanks
F.

You need to think outside of the box here.

Looking on your function that needs to get reused, what you want to do is the following.

Remove this piece of code to a function:

def do_someting():
    try:
        response = {"result": []}
        if data := self.level(request=request, departement=departement):
            levels_zones = self.response_to_json(data)

        for level_zone in levels_zones:
            params = {
                "target_date": datetime.now().strftime(self.format_date),
                "value": 9,
                "origin": "auto",
                "zm": level_zone.get("zm",0),
            }
            post_accesslevel = AccessViewSet.as_view({"post": "add"})(request._request, **params).render().content

    except ValueError as e:
        response = {"result": {"message_error": status.HTTP_406_NOT_ACCEPTABLE}}
    except Exception as e:
        response = {"result": {"message_error": status.HTTP_503_SERVICE_UNAVAILABLE}}
    finally:
        return Response(response)

You copy paste this on your editor it will blame that a lot of names does not exist, then you should pass then as parameters. Like so:

def do_something(request, other_arguments):
    ...

After that, on your viewset 1 and 2, you have to use that function:

Viewset 1:

@action(detail=True, methods=["POST"])
    def add(self, request, departement=None) -> Response:
        return do_something(request, other_arguments, go_here)

On Viewset 2 you need to call that function.