Djagno rest error: Невозможно использовать декоратор @action для следующих методов, поскольку они являются существующими маршрутами

Мне нужно написать набор представлений для профиля пользователя, в котором методы типа retrieve не принимают pk в качестве параметра, а получают id пользователя из токена аутентификации. но написав новое действие для этих методов:

class ProfileViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
GenericViewSet, ):
serializer_class = ProfileSerializer

    def get_object(self):
        return self.request.user
    
    @action(detail=False)
    def retrieve(self, request, *args, **kwargs):
        pass

возникает ошибка:

Cannot use the @action decorator on the following methods, as they are existing routes ...

одним из способов сделать это является написание пользовательских методов:

class ProfileViewSet(mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     GenericViewSet, ):
    serializer_class = ProfileSerializer

    def get_object(self):
        return self.request.user

    @action(detail=False)
    def retrieve_profile(self, request, *args, **kwargs):
        pass

но есть ли способ избежать этой ошибки и по-прежнему использовать методы по умолчанию?

ошибка вызвана классом маршрутизатора.

rest_framework.routers.SimpleRouter:

class SimpleRouter(BaseRouter):
    routes = [
        # List route.
        ...
    ]

    ...

    def get_routes(self, viewset):
        known_actions = list(flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)]))
        extra_actions = viewset.get_extra_actions()

        # checking action names against the known actions list
        not_allowed = [
            action.__name__ for action in extra_actions
            if action.__name__ in known_actions
        ]
        if not_allowed:
            msg = ('Cannot use the @action decorator on the following '
                   'methods, as they are existing routes: %s')
            raise ImproperlyConfigured(msg % ', '.join(not_allowed))
        ...

поэтому мы можем просто создать пользовательский маршрутизатор и изменить атрибут класса "routes".

первый путь

(обновленные части отмечены ######### changed)

class CustomRouter(DefaultRouter):
    """
    sets action as detail = False for all default urls
    """
    routes = [
        # List route.
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',
                'post': 'create'
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r'^{prefix}{trailing_slash}$',  ######### changed
            mapping={
                'get': 'retrieve',
                'put': 'update',
                'patch': 'partial_update',
                'delete': 'destroy'
            },
            name='{basename}-detail',
            detail=False,  ######### changed
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',  ######### changed
            name='{basename}-{url_name}',
            detail=False,  ######### changed
            initkwargs={}
        ),
    ]

views.py:

class ProfileViewSet(mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     GenericViewSet, ):
    serializer_class = ProfileSerializer

    def get_object(self):
        return self.request.user

urls.py:

profile_router = CustomRouter()
profile_router.register('profile', ProfileViewSet, basename='profile')

второй путь

class CustomRouter(DefaultRouter):
    routes = []

views.py:

class ProfileViewSet(mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     GenericViewSet, ):
    serializer_class = ProfileSerializer

    def get_object(self):
        return self.request.user
    
    @action(detail=False)
    def retrieve(self, request, *args, **kwargs):
        pass

urls.py: то же самое, что и в первом случае

Вернуться на верх