django - ViewSet 的动作装饰器忽略了它的 permission_classes
问题描述
为什么我的动作装饰器忽略了它的权限类?
我有一个具有“IsAuthenticated”的视图集,用于通用发布/创建和发布到自定义操作装饰器。
但是,当我为未登录的用户使用自定义动作装饰器时,动作装饰器的代码仍然运行(并导致错误)。
为什么是这样?未登录的用户在发布到操作时不应该收到 401_Unauthorized 吗?
未登录的用户在进行一般帖子时确实会收到 401_Unauthorized。
这是视图集:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
lookup_field = "pk"
serializer_class = ItemSerializer
def get_permissions(self):
if self.action == "create":
permission_classes = [IsAuthenticated]
else:
permission_classes = [AllowAny]
return [permission() for permission in permission_classes]
@action(
methods=["post"],
detail=True,
permission_classes=[IsAuthenticated],
url_name="actioned",
)
def actioned(self, request, pk=None):
try:
item = Item.objects.get(user=request.user)
item.status = "actioned"
item.save()
return Response(status=status.HTTP_200_OK)
except Item.DoesNotExist:
Item.objects.create(user=request.user, status="actioned")
return Response(status=status.HTTP_201_CREATED)
我正在为我的网址使用 DefaultRouter():
router = DefaultRouter
router.register(r"items", ItemViewSet, basename="item")
urlpatterns = [ path("", include(router.urls)), ]
以下是测试:
# GENERIC POST BY ANONYMOUSUSER DOESN'T RUN AND GIVES 401
def test_public_generic_post(self):
payload = {"status": None}
response = APIClient.post(
reverse("app:item-list"),
payload,
format="json"
)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) # True
# ACTION POST BY ANONYMOUSUSER RUNS EVEN THOUGH IT HAS THE SAME IsAuthenticated permission
def test_public_action_post(self):
item = Item.objects.create()
response = APIClient.post(
reverse("app:item-actioned", args=[item.pk]),
format="json"
)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) # False
对于操作帖子,我收到以下错误:
TypeError: Cannot cast AnonymousUser to int. Are you trying to use it in place of User?
因为它正在运行我的try: item = Item.objects.get(user=request.user)
代码并且没有request.user
.
但是,如果动作装饰器明确具有,为什么它会首先运行动作装饰器permission_classes=[IsAuthenticated]
呢?
我可以确认request.user.is_authenticated
是False
。
解决方案
根据@KutayAslan 的评论,删除 get_permissions 方法可以解决此错误。
看起来在我的原始代码中,动作装饰器正在分配 [AllowAny],因为它的“self.action”属于“else”子句。
将 permission_classes 设置为显式等于操作装饰器可解决此问题:
def get_permissions(self):
if self.action == "create":
permission_classes = [IsAuthenticated]
elif self.action == "actioned":
permission_classes = [IsAuthenticated]
else:
permission_classes = [AllowAny]
return [permission() for permission in permission_classes]
推荐阅读
- typescript - 扩大 TypeScript 中的 const 文字类型
- python - 删除出现在整个数据框中的特定值
- neo4j - 是否可以在 Cypher 中“重复”不止一跳的模式?
- android - 在 Android 中从 File Api 迁移到 Storage Access Framework (SAF)
- sql - 使用 SQL Alchemy 会话注册用户定义函数
- reactjs - 音频到 blob 和 POST 到 cloudinary 问题
- django - 如何在 django 中限制对媒体图像 URL 的访问
- jquery - 画布元素在 WordPress 中不起作用
- swift - 如何快速生成不重叠的随机按钮
- python-3.x - matplotlib 中的用户警告