Django DRF@action报错:意外关键字参数'id'问题求助
Django REST Framework 订阅功能报错排查
问题现象
调用接口/api/users/11/subscribe/时触发以下错误:
TypeError: set_or_delete_subscribe() got an unexpected keyword argument 'id' [10/Mar/2025 08:38:23] "POST /api/users/11/subscribe/ HTTP/1.0" 500 88724
相关代码
CustomUserViewSet 实现
class CustomUserViewSet(UserViewSet): serializer_class = UserSerializer pagination_class = PageNumberPagination permission_classes = [permissions.IsAuthenticated] def get_queryset(self): queryset = User.objects.all() return queryset @action(detail=False, methods=['put', 'delete'], url_path='me/avatar') def set_avatar(self, request): if request.method == 'PUT': serializer = SetAvatarSerializer( request.user, data=request.data, partial=True ) serializer.is_valid(raise_exception=True) serializer.save() return Response(status=status.HTTP_200_OK) user = request.user user.avatar.delete() user.save() return Response(status=status.HTTP_204_NO_CONTENT) @action(detail=True, methods=['post', 'delete'], url_path='subscribe') def set_or_delete_subscribe(self, request, pk=None): user = request.user user_to_subscribe = self.kwargs['id'] if request.method == 'POST': _, created = Subscription.objects.get_or_create(user=user, subscribed=user_to_subscribe) if created: return Response(status=status.HTTP_201_CREATED) return Response(status=status.HTTP_400_BAD_REQUEST) if request.method == 'DELETE': subscription = Subscription.objects.filter(user=user, subscribed=user_to_subscribe).delete() if subscription: return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_400_BAD_REQUEST)
Subscription 模型定义
class Subscription(models.Model): user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='subscriptions' ) subscribed = models.ForeignKey( User, on_delete=models.CASCADE, related_name='subscribed' ) class Meta: constraints = [ models.UniqueConstraint(fields=['user', 'subscribed'], name='unique_subscription') ]
URL 路由配置
router = DefaultRouter() router.register(r'tags', TagViewSet, basename='tags') router.register(r'recipes', RecipeViewSet, basename='recipes') router.register(r'ingredients', IngridientsViewSet, basename='ingredients') router.register(r'users', CustomUserViewSet, basename='users')
问题原因及修复方案
核心问题
- 参数签名不匹配:DRF 默认会根据模型主键字段名传递参数(如果User模型主键是
id,路由会传递id参数),但你的视图方法签名定义的是pk=None,导致方法无法接收id参数,触发TypeError。 - 用户实例获取错误:直接从
self.kwargs['id']拿到的是字符串类型的ID值,不能直接传给Subscription模型的subscribed字段(该字段需要User实例)。
修复步骤
步骤1:统一参数与实例获取逻辑
推荐使用DRF内置的get_object()方法获取目标用户,自动处理权限校验和404错误,同时修正方法签名匹配路由参数:
@action(detail=True, methods=['post', 'delete'], url_path='subscribe') def set_or_delete_subscribe(self, request, id=None): user = request.user # 用get_object()获取目标用户实例 user_to_subscribe = self.get_object() if request.method == 'POST': _, created = Subscription.objects.get_or_create(user=user, subscribed=user_to_subscribe) if created: return Response(status=status.HTTP_201_CREATED) return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": "已订阅该用户"}) if request.method == 'DELETE': deleted_count, _ = Subscription.objects.filter(user=user, subscribed=user_to_subscribe).delete() if deleted_count > 0: return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": "未订阅该用户"})
步骤2:优化错误判断
filter().delete()返回的是(删除记录数, 模型类型统计)的元组,需通过判断删除记录数是否大于0来确认操作是否成功,而非直接判断返回值。
验证修复
重启服务后,调用POST /api/users/11/subscribe/可正常创建订阅,DELETE请求可正常取消订阅,不再触发TypeError。
内容的提问来源于stack exchange,提问作者st.splash
相关产品推荐
相关产品推荐

