Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AttributeError: 'user_list' object has no attribute '_ignore_model_permissions'

I'm going through the django REST framework tutorial, except replacing the Snippet model with my own custom User model. However, when I try to test out the API, I keep getting

Traceback (most recent call last):
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/rest_framework/views.py", line 326, in dispatch
    response = self.handle_exception(exc)
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/rest_framework/views.py", line 314, in dispatch
    self.initial(request, *args, **kwargs)
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/rest_framework/views.py", line 235, in initial
    self.check_permissions(request)
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/rest_framework/views.py", line 192, in check_permissions
    if not permission.has_permission(request, self):
  File "/Users/AndyFang/Desktop/doorstep-django/venv/lib/python2.7/site-packages/rest_framework/permissions.py", line 131, in has_permission
    if model_cls is None and getattr(view, '_ignore_model_permissions'):
AttributeError: 'user_list' object has no attribute '_ignore_model_permissions'

I've tried various things to fix this (such as disabling the format feature, adding csrf_exmept token, etc.) all to no avail. Below is the code I wrote:

serializers.py

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'email', 'phone_number', 'first_name', 'last_name')

views.py

@api_view(['GET', 'POST'])
def user_list(request, format=None):
  """
  List all users, or create a new user.
  """
  if request.method == 'GET':
    users = User.objects.all()
    serializer = UserSerializer(users, many=True)
    return Response(serializer.data)

  elif request.method == 'POST':
    serializer = UserSerializer(data=request.DATA)
    if serializer.is_valid():
      serializer.save()
      return Response(serializer.data, status=status.HTTP_201_CREATED)
    else:
      return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PUT', 'DELETE'])
def user_detail(request, pk, format=None):
  """
  Retrieve, update or delete a user instance.
  """              
  try:
    user = User.objects.get(pk=pk)
  except User.DoesNotExist:
    return Response(status=status.HTTP_404_NOT_FOUND)

  if request.method == 'GET':
    serializer = UserSerializer(user)
    return Response(serializer.data)

  elif request.method == 'PUT':
    serializer = UserSerializer(user, data=request.DATA)
    if serializer.is_valid():
      serializer.save()
      return Response(serializer.data)
    else:
      return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

  elif request.method == 'DELETE':
    user.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)

urls.py

like image 581
fangsterr Avatar asked Jun 18 '13 04:06

fangsterr


2 Answers

That's a bit of a bug there, AttributeError shouldn't be raised there - I've just fixed that in master now.

Instead what you'll see when you run against master now is this assertion error:

Cannot apply DjangoModelPermissions on a view that does not have `.model` or `.queryset` property.

The issue here is that the DjangoModelPermissions class can only work if it has a way of determining which model permissions are required for the view. If you want to use that permission class, you'll need to re-write your views as class based views, and ensure they have the model attribute set appropriately.

Note that just because you re-write them as class-based views that doesn't mean you need to use the generic class based views, you can still keep the explicit view logic you currently have. Something like this:

class UserListView(views.APIView):
    queryset = User.objects.all()

    def get(self, request, format=None):
        users = self.queryset
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):     
        serializer = UserSerializer(data=request.DATA)
        if serializer.is_valid():
          serializer.save()
          return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class UserDetailView(views.APIView):
    queryset = User.objects.all()

    def get_object(self, pk)
        try:
            return self.queryset.get(pk=pk)
        except User.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        user = self.get_object(pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        user = self.get_object(pk)
        serializer = UserSerializer(user, data=request.DATA)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        user = self.get_object(pk)
        user.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Note the use of queryset in both cases means the DjangoModelPermissions will now be able to be applied to these views.

like image 115
Tom Christie Avatar answered Oct 19 '22 03:10

Tom Christie


You probably have the DjangoModelPermissions or similar set as a default permission class in your settings. Something like:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
    )
}

This will by default check if a user has permissions on the model your view is based on. If you view doesn't have a model set on it, it can't find out the permissions. So you need to set the model on the view. This is as far as I can see only possible in a class based APIView:

from .models import MyModel

class MyView(APIView):

    model = MyModel

    def get(self, request, pk):
        # ...

In case your view is not based on a Django model, you can either remove this default class from your REST_FRAMEWORK settings, or set a custom permission class on your view, like:

from rest_framework import permissions

class MyView(APIView)

    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, pk):
        # ...

Or in a function based view as:

from rest_framework import permissions

@api_view(['GET', 'POST'])
@permission_classes((permissions.IsAuthenticated,))
def my_view(request)
    # ...

For more info about permissions see http://django-rest-framework.org/api-guide/permissions.html

like image 28
gitaarik Avatar answered Oct 19 '22 01:10

gitaarik