'Endpoints sharing part of an URL
I'm currently building an API with Django Rest Framework and I want to have two viewsets. One related to my User model, and one single route to verify a User email (you POST the currently-valid token to the endpoint). So it looks like this:
- UserViewSet, which allows POST, GET, PUT, PATCH, DELETE
- VerifyUserViewSet, which only allows POST
In my router, here's my config:
router = routers.DefaultRouter()
router.register("users", UserViewSet, "users")
router.register("users/verify", VerifyUserViewset, "users/verify")
My problem is as follows: "users/verify" allows a GET request (when it shouldn't) because it is targeting the "users" endpoint with "request" as the user id.
If I change the order in my router, then "users" only accept POST because the endpoints targets "users/verify". This seems to be happening because they share the same url part "users".
Is there a way to bypass this behavior so that they don't overlap without changing the endpoints url?
Thanks
EDIT
Serializer:
class VerifyUserSerializer(serializers.ModelSerializer):
# ----------------------------------------
# Meta, create, update
# ----------------------------------------
class Meta:
model = UserProfile
fields = ["verification_token"]
# ----------------------------------------
# Custom Validation
# ----------------------------------------
def validate_verification_token(self, token):
"""Makes the token required and compare its value to the database"""
# Check empty
message = gettext("'verification_token' is required")
token = is_empty(token, message)
# Find matching user
user = try_get_object(UserProfile, verification_token=token)
if not user:
message = gettext("Invalid Verification Token")
raise serializers.ValidationError(message)
# Return unchanged
return token
Viewset:
class VerifyUserViewset(viewsets.GenericViewSet):
# ----------------------------------------
# Settings
# ----------------------------------------
permission_classes = (permissions.AllowAny, )
serializer_class = VerifyUserSerializer
# ----------------------------------------
# Actions
# ----------------------------------------
def create(self, request, *args, **kwargs):
"""POST: Verify the user using its token"""
# Form validation
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# Validate the user
token = serializer.validated_data["verification_token"]
user = UserProfile.objects.get(verification_token=token)
user.validation_email_verification()
return Response(None, status=status.HTTP_202_ACCEPTED)
Solution 1:[1]
I've found the solution in the Django Rest Framework API Guide: https://www.django-rest-framework.org/api-guide/routers/#routing-for-extra-actions
Since I have a "users/" endpoint and want to user a "users/verify/" endpoint, I must add an action to my UserViewset (the one bound to "users/") that points to /verify/.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Jordan Kowal |