django-experience

Tutorial Django Experience 2022

View the Project on GitHub rg3915/django-experience

Django Experience #07 - DRF: APIView e o problema do get_extra_actions

Criando uma app para o exemplo

Antes vamos considerar a app example, um model Example e um campo title.

python manage.py dr_scaffold example Example title:charfield
mv example backend
mkdir backend/example/api
mv backend/example/serializers.py backend/example/api
mv backend/example/views.py backend/example/api/viewsets.py

Edite example/apps.py

name = 'backend.example'

Edite settings.py

INSTALLED_APPS = [
    # my apps
    'backend.core',
    'backend.example',
    ...
]

Edite urls.py principal

urlpatterns = [
    path('', include('backend.core.urls', namespace='core')),
    path('', include('backend.example.urls', namespace='example')),
    ...
]

Edite example/admin.py

from backend.example.models import Example

Edite example/urls.py

from django.urls import include, path
from rest_framework import routers

from backend.example.api.viewsets import ExampleViewSet

app_name = 'example'

router = routers.DefaultRouter()

router.register(r'examples', ExampleViewSet, basename='example')

urlpatterns = [
    path('api/v1/', include(router.urls)),
]

Edite example/api/serializers.py

from backend.example.models import Example

Edite example/api/viewsets.py

from backend.example.models import Example
from backend.example.api.serializers import ExampleSerializer

Finalmente rode

python manage.py makemigrations
python manage.py migrate

O problema do get_extra_actions

Existem muitos tutoriais na internet, inclusive na documentação, tentando exemplificar de uma forma mais simples, o uso da APIView. Como no exemplo a seguir:

class ExampleView(APIView):

    def get(self, request, format=None):
        content = {
            'user': str(request.user),  # `django.contrib.auth.User` instance.
            'auth': str(request.auth),  # None
        }
        return Response(content)

Uma coisa que não está escrito lá, é que ao usá-lo, ele gera o seguinte erro:

AttributeError: 'function' object has no attribute 'get_extra_actions'

E mesmo que você tente implementar esse método, por exemplo:

class ExampleView(APIView):
    ...

    @classmethod
    def get_extra_actions(cls):
        return []

De nada adianta. Na verdade o problema está no routers, que não reconhece esse método, mesmo que você o implemente. Ou seja, se você usar

from backend.example.api.viewsets import ExampleView

router = routers.DefaultRouter()

router.register(r'examples', ExampleView, basename='example')

… ele não vai funcionar.

Qual é a solução?

urlpatterns = [
    # path('api/v1/', include(router.urls)),
    path('api/v1/examples/', ExampleView.as_view()),
]

E não precisa implementar o get_extra_actions.

Resolvido o problema. :)