Django REST Framework - 使用 @action 装饰器定义额外参数

问题描述 投票:0回答:5

我想知道是否可以使用

action
装饰器定义额外的参数:

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(methods=['post'], detail=True)
    def follow(self, request, pk=None):
        user = self.get_object()
        target_user = ???
        Follow.objects.create(user=user, target=target_user)
        return Response(status=status.HTTP_204_NO_CONTENT)

我想要实现的是匹配以下路径:

api/users/{id}/follow/{target_id}

follow
操作将用于让 ID 为
id
的用户关注另一个 ID 为
target_id
的用户。

更新

由于似乎无法传递额外的参数,所以现在我将数据作为要遵循的用户 ID 列表发布:

用于验证数据的序列化器:

class FollowSerializer(serializers.ModelSerializer):
    user_ids = serializers.ListField(child=serializers.IntegerField(min_value=1), required=False, help_text='User target IDs')

行动:

    @action(detail=True, methods=['post'])
    def follow(self, request, pk=None):
        user = self.get_object()
        serializer = FollowSerializer(data=request.data)
        if serializer.is_valid():
            serializer.data['user_ids']
            for user_id in user_ids:
                target_user = User.objects.get(pk=user_id)
                Follow.objects.create(user=user, target=target_user)
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

我的解决方案的问题是,在自动生成的文档页面上,我看到带有

UserSerializer
字段的表单。

API Doc Page

更新2

下面的技巧让自动生成的文档页面显示正确的形式:

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()

    def get_serializer_class(self):
        if self.action == 'follow':
            return FollowSerializer
        else:
            return UserSerializer

    @action(methods=['post'], detail=True)
    def follow(self, request, pk=None):
        ...

API Doc Page - Correct Form

django-rest-framework
5个回答
26
投票

您可以通过 url 传递

target_id
api/users/{id}/follow/{target_id}
,但您必须将视图更改为,

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(methods=['post'], detail=True)
    def follow(self, request, *args, **kwargs):
        user = self.get_object()
        target_user = int(kwargs['target_id'])
        Follow.objects.create(user=user, target=target_user)
        return Response(status=status.HTTP_204_NO_CONTENT)

并在 urls.py 中定义一个

单独的 path() 为,

urlpatterns = [
                  path('users/<int:pk>/follow/<int:target_id>/', UserViewSet.as_view({"post": "follow"}))

              ]

24
投票

我的解决方案的问题是,在自动生成的文档页面上,我看到带有 UserSerializer 字段的表单。

来自文档

装饰器还可以采用仅为路由视图设置的额外参数。例如:

这意味着任何可以用作类属性的东西也可以用作

@action()
的参数,包括
serializer_class
:

@action(methods=['post'], detail=True, serializer_class=FollowSerializer)
def follow(self, request, pk=None):
    # ...
    serializer = self.get_serializer(data=request.data)
    # ...

这将导致自动生成的文档中出现正确的表单。


10
投票

事实上,您可以接受额外的 kwargs,如下 DRF 3.13.x,无需接触路由器,简洁的 DRF:

@action(detail=True, methods=['delete'], url_name='media-delete',
        url_path='delete_media/(?P<media_pk>[^/.]+)')
def delete_media(self, request, pk=None, media_pk=None):
    # do your logic
    return Response({}, status=status.HTTP_202_ACCEPTED)

这将自动覆盖默认路由器额外路由,以响应 @action 包装器和 ModelViewSet 注册的路由。


0
投票

我不确定。查看文档,似乎 @action() 只接受 .get_extra_actions() 获得的特定操作列表

您可以直接使用目标的电子邮件/帐户名称/帐户 ID 作为关注目标,然后发布该数据吗?无论如何,这似乎是更简单的方法。


0
投票

defapprove_quotation(self, request, pk=None): 引用 = self.get_object()

    # Assuming QuotationStatus has a unique constraint on name field
    status_instance = get_object_or_404(QuotationStatus, name='Approved')
    
    quotation.status = status_instance
    quotation.save()

    serializer = self.get_serializer(quotation)
    return Response(serializer.data)

我无法更新状态

© www.soinside.com 2019 - 2024. All rights reserved.