84

I am currently developing an API using Django.

However, I would like to create a view that returns the current User with the following endpoint: /users/current/.

To do so, I created a list view and filtered the queryset on the user that made the request. That works, but the result is a list, not a single object. Combined with pagination, the result looks way too complicated and inconsistent compared to other endpoints.

I also tried to create a detail view and filtering the queryset, but DRF complains that I provided no pk or slug.

Francisco
  • 10,918
  • 6
  • 34
  • 45
Maxime
  • 1,776
  • 1
  • 16
  • 29

6 Answers6

94

With something like this you're probably best off breaking out of the generic views and writing the view yourself.

@api_view(['GET'])
def current_user(request):
    serializer = UserSerializer(request.user)
    return Response(serializer.data)

You could also do the same thing using a class based view like so...

class CurrentUserView(APIView):
    def get(self, request):
        serializer = UserSerializer(request.user)
        return Response(serializer.data)

Of course, there's also no requirement that you use a serializer, you could equally well just pull out the fields you need from the user instance.

@api_view(['GET'])
def current_user(request):
    user = request.user
    return Response({
        'username': user.username,
        'email': user.email,
        ...
    })
starball
  • 20,030
  • 7
  • 43
  • 238
Tom Christie
  • 33,394
  • 7
  • 101
  • 86
  • 2
    Note: I had to use `serializer = UserSerializer(request.user, context={'request': request})` because my user serializer includes a hyperlink `url` field (based on the default project). – Sam Watkins Jan 28 '21 at 11:57
37

The best way is to use the power of viewsets.ModelViewSet like so:

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get_object(self):
        pk = self.kwargs.get('pk')

        if pk == "current":
            return self.request.user

        return super().get_object()

viewsets.ModelViewSet is a combination of mixins.CreateModelMixin + mixins.RetrieveModelMixin + mixins.UpdateModelMixin + mixins.DestroyModelMixin + mixins.ListModelMixin + viewsets.GenericViewSet. If you need just list all or get particular user including currently authenticated you need just replace it like this

class UserViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
    # ...
Francisco
  • 10,918
  • 6
  • 34
  • 45
Vladimir Prudnikov
  • 6,974
  • 4
  • 48
  • 57
13

If you must use the generic view set for some reason, you could do something like this,

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user

    def list(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

retrieve method is called when the client requests a single instance using an identifier like a primary key /users/10 would trigger the retrieve method normally. Retrieve itself calls get_object. If you want the view to always return the current used then you could modify get_object and force list method to return a single item instead of a list by calling and returning self.retrieve inside it.

Francisco
  • 10,918
  • 6
  • 34
  • 45
Owais Lone
  • 658
  • 1
  • 8
  • 11
13

Instead of using full power of ModelViewSet you can use mixins. There is RetrieveModelMixin used to retrieve single object just like it is mentioned here - http://www.django-rest-framework.org/api-guide/viewsets/#example_3

class UserViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user

If you need also update your model, just add UpdateModelMixin.

Francisco
  • 10,918
  • 6
  • 34
  • 45
ollamh
  • 360
  • 3
  • 6
9

I used a ModelViewSet like this:

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    
    def dispatch(self, request, *args, **kwargs):
        if kwargs.get('pk') == 'current' and request.user:
            kwargs['pk'] = request.user.pk

        return super().dispatch(request, *args, **kwargs)
Francisco
  • 10,918
  • 6
  • 34
  • 45
ejb
  • 304
  • 2
  • 3
  • 1
    elb, i tried to use the above function but it seems like dispatch() runs too early that request.user is still anonymous, user is assigned after the dispatch is ran.. any alternative for that? – Mo J. Mughrabi Dec 31 '13 at 11:40
  • This one works fine for me. You may want to change `request.user` to `request.user.is_authenticated()`. Cuz `request.user` is `AnonymousUser` if you are not logged in. – Greg Wang Mar 29 '14 at 18:45
  • 3
    Works with SessionAuthentication. Doesn't work when using TokenAuthentication. request.user is anonymous inside dispatch. – sudokai Aug 10 '15 at 17:12
  • This will not work with Token authentication. Where authentication is performed is inside of initial() call, and that is done in dispatch, so use will never be authenticated. – pjotr_dolphin Jun 30 '17 at 10:07
2

Use this way to get logged in user data in django rest framework

class LoggedInUserView(APIView):
    def get(self, request):
        serializer = UserSerializer(self.request.user)
        return Response(serializer.data)

Add the api in urls.py file.

path('logged_in_user', LoggedInUserView.as_view())
Sathiamoorthy
  • 8,831
  • 9
  • 65
  • 77