Reputation: 4255
I am using the basic UserViewSet derived from ModelViewSet.
Retrieving a user with a primary-key via api/users/<pk>
works fine.
But I also want to be able to retrieve a User by Username.
I have added a new detail route but I always get 404
on my server when I try to get the user with the url /api/users/retrieve_by_username/altoyr
.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@detail_route(methods=['get'])
def retrieve_by_username(self, request, username=None):
try:
user = User.objects.get(userName=username)
return Response(user)
except User.DoesNotExist:
return Response("No user with username found!", status=status.HTTP_400_BAD_REQUEST)
The Urls are registered via a router:
router = DefaultRouter()
router.register(r'users', views.UserViewSet)
# The API URLs are now determined automatically by the router.
# Additionally, we include the login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
I think I am missing an important part of building rest urls.
Upvotes: 3
Views: 5606
Reputation: 13
The answer from Anush Devendra is right but need a little update due to deprecations on v3.9.
action decorator replaces
list_route
anddetail_route
Bothlist_route
anddetail_route
are now deprecated in favour of the single action decorator. They will be removed entirely in 3.10. The action decorator takes a boolean detail argument.
- Replace detail_route uses with @action(detail=True).
- Replace list_route uses with @action(detail=False).
...
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from rest_framework import status
class UserViewSet(viewsets.ModelViewSet):
...
@action(methods=['get'], detail=False,
url_path='username/(?P<username>\w+)')
def getByUsername(self, request, username):
user = get_object_or_404(User, username=username)
data = UserSerializer(user, context={'request': request}).data
return Response(data, status=status.HTTP_200_OK)
I add context={'request': request}
because I have url
as HyperlinkedIdentityField in my serializer. If you don't have it, you probably don't need it.
Upvotes: 1
Reputation: 21
You can override the retrieve() ViewSet action. You'll find more detail here: https://www.django-rest-framework.org/api-guide/viewsets/
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def retrieve(self, request, pk=None):
queryset = User.objects.filter(username=pk)
contact = get_object_or_404(queryset, pk=1)
serializer = ContactSerializer(contact)
return Response(serializer.data)
Upvotes: 2
Reputation: 5475
You can do this by adding a list route like:
@list_route(methods=['get'], url_path='retrieve_by_username/(?P<username>\w+)')
def getByUsername(self, request, username ):
user = get_object_or_404(User, username=username)
return Response(UserSerializer(user).data, status=status.HTTP_200_OK)
and the url will be like:
/api/users/retrieve_by_username/altoyr
Upvotes: 10