I'm wondering if would be possible to define extra arguments using the action
decorator:
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)
What I want to achieve is matching the following path: api/users/{id}/follow/{target_id}
The follow
action will be used to let an user with ID id
following another user with ID target_id
.
UPDATE:
Since it seems like it's not possible to pass extra args, right now I'm POSTing data as a list of user IDs to be followed:
The serializer to validate data:
class FollowSerializer(serializers.ModelSerializer):
user_ids = serializers.ListField(child=serializers.IntegerField(min_value=1), required=False, help_text='User target IDs')
The action:
@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)
The problem with my solution is that on the auto-generated doc page I'm seeing the form with the UserSerializer
fields.
UPDATE 2:
The following trick let the auto-generated doc page shows the right form:
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):
...
You could pass target_id
through url as,api/users/{id}/follow/{target_id}
, but you have to change the views as,
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)
and define a seperate path()
in urls.py as,
urlpatterns = [
path('users/<int:pk>/follow/<int:target_id>/', UserViewSet.as_view({"post": "follow"}))
]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With