I am trying to create a ModelViewSet for supporting CRUD operations on my address model. I am able to successfully use LIST and CREATE endpoints, but all the other 4 endpoints which require/id/ in their URL return me the error -
{ "detail": "You do not have permission to perform this action." }
The address model should support the operations only by the Users who are marked NOT SELLER in the DB, hence the ~IsSeller custom permission is applied.
The issue goes away when I remove this custom permission from permissions classes, but i need to keep this restriction of only non-seller users to be able to add the address.
permissions.py
from rest_framework.permissions import BasePermission
class IsSeller(BasePermission):
def has_permission(self, request, view):
return (request.user and request.user.is_seller)
Views.py
class ManageAddressViewSet(viewsets.ModelViewSet):
serializer_class = AddressSerializer
permission_classes = [permissions.IsAuthenticated, (~IsSeller)]
queryset = Address.objects.all()
def get_queryset(self):
return self.queryset.filter(user=self.request.user)
def perform_create(self, serializer):
return serializer.save(user=self.request.user)
Serializers.py
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = [
'id', 'name', 'line_1',
'line_2', 'city', 'state',
'pincode', 'type'
]
read_only_fields = ['id']
urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from user import views
router = DefaultRouter()
router.register('address', views.ManageAddressViewSet)
urlpatterns = [
path('create/', views.CreateUserView.as_view(), name='create'),
path('authenticate/', views.CreateTokenView.as_view(), name='authenticate'),
path('my-profile/', views.ManageUserView.as_view(), name='my-profile'),
path('address/', include(router.urls)),
Models.py
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser,
PermissionsMixin,
BaseUserManager
)
from django.conf import settings
ADDRESS_CHOICES = (
('WORK', 'Work',),
('HOME', 'Home',),
('Other', 'Other',),
)
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **other_fields):
if not email:
raise (ValueError("Email not provided"))
user = self.model(
email=self.normalize_email(email),
**other_fields
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
number = models.CharField(max_length=15, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_seller = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
objects = UserManager()
class Address(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
name = models.CharField(max_length=255)
line_1 = models.CharField(max_length=255)
line_2 = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=255)
state = models.CharField(max_length=255)
pincode = models.CharField(max_length=6)
type = models.CharField(
max_length=20,
choices=ADDRESS_CHOICES,
default='HOME'
)
def __str__(self):
return self.name
Endpoints
GET(working) /api/user/address/address/
POST(working) /api/user/address/address/
GET(permission error) /api/user/address/address/{id}/
PUT(permission error) /api/user/address/address/{id}/
PATCH(permission error) /api/user/address/address/{id}/
DELETE(permission error) /api/user/address/address/{id}/
It's most likely to do with the combined facts that you have not implemented the has_object_permission
method, and are also using the ~
negation.
See here for a discussion on the DRF repo on this very issue: https://github.com/encode/django-rest-framework/issues/6598
I suggest you implement the extra method, most likely just returning the same result as the has_permission
method, i.e.
def has_object_permission(self, request, view, obj):
return self.has_permission(request, view)