Django viewset has not attribute 'get_extra_actions'

64,279

Solution 1

You've called it a viewset, but that doesn't make it one; you inherit from APIView which is a standalone generic view, not a viewset.

A viewset needs to inherit from viewsets.ViewSet.

Solution 2

Before Django Rest Framework v3.8 you could register an APIView directly with a router. I did this extensively to gain a nice collated (and versioned) auto-documenting API for some very custom API endpoints. Given the choice again, I would probably write the whole thing a more standard way, but that isn't an option for everybody.

But after digging into the error, it turns out you can just patch over the problem by giving the router what it wants and adding a dummy get_extra_actions classmethod.

class MyAPIView(APIView):

    @classmethod
    def get_extra_actions(cls):
        return []

#...

I'm not saying this is good, but it works for now.
I've got my documentation back and I've managed to upgrade to DRFv3.8.

Solution 3

For:

djangorestframework==3.11.0
Django==2.2.9

You need to change class SessionViewSet(APIView): to:

from rest_framework import mixins, viewsets

class SessionViewSet(mixins.ListModelMixin,
                     viewsets.GenericViewSet):

To get it to work. The internals of DRF have changed a bit and the other solutions won't cut it any longer.

Solution 4

In views.py, your viewset have to inherit from viewset and use it in your viewset try code below:

class SessionViewSet(viewsets.ModelViewSet):
    queryset = Session.objects.all()
    serializer_class = SessionSerializer

    def get(self, request, format=None):
        return Response("test")

Solution 5

Beware of using the same names for your viewset class and your model class. This was the reason for my own error. See example of what i did

# inside member/views.py
from member.models import Member

# inheriting from model viewset but called Member
class Member(viewsets.ModelViewSet):
    queryset = Member.objects.all()
    ...

# inside urls.py
from member.views import Member

router = routers.DefaultRouter()
router.register(r'member', Member)

Now the mistake here is its importing the member model instead of the viewset but they are of the same names

Share:
64,279

Related videos on Youtube

alexca
Author by

alexca

Updated on July 09, 2022

Comments

  • alexca
    alexca almost 2 years

    I am working with Django for a first time and I'm trying to build an API and I am following some tutorials and examples and it works right, but I am running the project now in a Raspberry Pi after install all the requirements and the project is failing with the following error:

        Performing system checks...
    
    Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0xb547adb0>
    Traceback (most recent call last):
      File "/home/pi/.local/lib/python3.5/site-packages/django/utils/autoreload.py", line 225, in wrapper
        fn(*args, **kwargs)
      File "/home/pi/.local/lib/python3.5/site-packages/django/core/management/commands/runserver.py", line 120, in inner_run
        self.check(display_num_errors=True)
      File "/home/pi/.local/lib/python3.5/site-packages/django/core/management/base.py", line 364, in check
        include_deployment_checks=include_deployment_checks,
      File "/home/pi/.local/lib/python3.5/site-packages/django/core/management/base.py", line 351, in _run_checks
        return checks.run_checks(**kwargs)
      File "/home/pi/.local/lib/python3.5/site-packages/django/core/checks/registry.py", line 73, in run_checks
        new_errors = check(app_configs=app_configs)
      File "/home/pi/.local/lib/python3.5/site-packages/django/core/checks/urls.py", line 13, in check_url_config
        return check_resolver(resolver)
      File "/home/pi/.local/lib/python3.5/site-packages/django/core/checks/urls.py", line 23, in check_resolver
        return check_method()
      File "/home/pi/.local/lib/python3.5/site-packages/django/urls/resolvers.py", line 397, in check
        for pattern in self.url_patterns:
      File "/home/pi/.local/lib/python3.5/site-packages/django/utils/functional.py", line 36, in __get__
        res = instance.__dict__[self.name] = self.func(instance)
      File "/home/pi/.local/lib/python3.5/site-packages/django/urls/resolvers.py", line 536, in url_patterns
        patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
      File "/home/pi/.local/lib/python3.5/site-packages/django/utils/functional.py", line 36, in __get__
        res = instance.__dict__[self.name] = self.func(instance)
      File "/home/pi/.local/lib/python3.5/site-packages/django/urls/resolvers.py", line 529, in urlconf_module
        return import_module(self.urlconf_name)
      File "/usr/lib/python3.5/importlib/__init__.py", line 126, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 986, in _gcd_import
      File "<frozen importlib._bootstrap>", line 969, in _find_and_load
      File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 673, in exec_module
      File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
      File "/home/pi/Projects/openvpn-monitor/openvpnmonitor/urls.py", line 24, in <module>
        url(r'^api/', include('openvpnmonitor.api.urls')),
      File "/home/pi/.local/lib/python3.5/site-packages/django/urls/conf.py", line 34, in include
        urlconf_module = import_module(urlconf_module)
      File "/usr/lib/python3.5/importlib/__init__.py", line 126, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 986, in _gcd_import
      File "<frozen importlib._bootstrap>", line 969, in _find_and_load
      File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 673, in exec_module
      File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
      File "/home/pi/Projects/openvpn-monitor/openvpnmonitor/api/urls.py", line 16, in <module>
        urlpatterns += router.urls
      File "/home/pi/.local/lib/python3.5/site-packages/rest_framework/routers.py", line 101, in urls
        self._urls = self.get_urls()
      File "/home/pi/.local/lib/python3.5/site-packages/rest_framework/routers.py", line 363, in get_urls
        urls = super(DefaultRouter, self).get_urls()
      File "/home/pi/.local/lib/python3.5/site-packages/rest_framework/routers.py", line 261, in get_urls
        routes = self.get_routes(viewset)
      File "/home/pi/.local/lib/python3.5/site-packages/rest_framework/routers.py", line 176, in get_routes
        extra_actions = viewset.get_extra_actions()
    AttributeError: type object 'SessionViewSet' has no attribute 'get_extra_actions'
    

    My views.py has the following code:

    from django.shortcuts import render
    
    from rest_framework import viewsets
    from .models import Session
    from .serializers import SessionSerializer
    
    from rest_framework.views import APIView, Response
    
    
    class SessionViewSet(APIView):
        queryset = Session.objects.all()
        serializer_class = SessionSerializer
    
        def get(self, request, format=None):
            return Response("test")
    

    I really don't know why is working on my laptop but it is not working on my Raspberry Pi.

    Has this happened to someone or anyone knows why is happening this?

    Thank you so much!

    Edit:

    Here is my urls.py

    from django.conf.urls import url
    from rest_framework import routers
    from openvpnmonitor.api.views import SessionViewSet
    
    router = routers.DefaultRouter()
    router.register(r'sessions', SessionViewSet)
    
    urlpatterns = [
        url(r'sessions', SessionViewSet.as_view()),
        url(r'^docs/', schema_view),
    ]
    
    urlpatterns += router.urls
    
  • Daniel Roseman
    Daniel Roseman about 6 years
    I don't understand what you mean. That tutorial creates both viewsets and a standalone APIView; you seem to be mixing up the two methods.
  • alexca
    alexca about 6 years
    Yes, that tutorial creates both viewsets and a standalone APIView, I've created just a APIView to have a custom view and have just the GET endpoint instead of the CRUD, and that's working in my laptop, I mean, if I run the project on my laptop it works right, but I'm trying to run the same project on my raspberry pi and I'm getting that error. I've just tried to change the APIView to a ViewSet on the Raspberry Pi and it works, but I want to use an APIView to have an endpoint with just the GET endpoint.
  • Daniel Roseman
    Daniel Roseman about 6 years
    You need to show your urls.py; as you can see from the tutorial, viewsets and standalone views are referenced differently in the urls.
  • alexca
    alexca about 6 years
    Thank you Daniel, I just edited my question and I've added my urls.py
  • Daniel Roseman
    Daniel Roseman about 6 years
    You're registering that view twice. You don't need a router for a standalone view. (Note, you really shouldn't call it a viewset in the first place if it is not one.)
  • alexca
    alexca about 6 years
    Thank you Daniel!! I was reading a bit more about APIView once you said that I don't need a router for a standalone view and I modified my urls.py using format_suffix_patterns instead of routes and It works now! thank you!
  • pass-by-ref
    pass-by-ref over 5 years
    The most important statement in the explanation is - You don't need a router for a standalone view. This statement solved all my problems. Thanks.
  • Oleg Belousov
    Oleg Belousov over 4 years
    Hi! I am using DRF 3.10.2 with Django 2.2.4. Adding this stub method results in the router ignoring this route :(
  • MartinP
    MartinP about 4 years
    typo, viewsets.ViewSet
  • rmcsharry
    rmcsharry about 4 years
    As of Apr 2020, this is the up-to-date answer. Thank you!
  • Thusjanthan Kubendranathan
    Thusjanthan Kubendranathan about 3 years
    If you look at the DRF viewsets.py, it does extend from APIView and ViewSetMixin, the latter implements the get_extra_actions and so if you extend from viewsets.ViewSet, that should be good.