Access current user from viewsets.ViewSet - django-rest-framework

I am trying to access current user from List method in viewsets.ViewSet. But I am getting AnonymousUser.
I have tried this
class ReportViewSet(viewsets.ViewSet):
"""Shows purchase report group by day"""
def list(self, request, **kwargs):
print(self.request.user)
Is there any way to access current user from viewsets.ViewSet?

Solved
from rest_framework.authentication import TokenAuthentication
class ReportViewSet(viewsets.ViewSet):
"""Shows purchase report group by day"""
authentication_classes = (TokenAuthentication,)
def list(self, request, **kwargs):
print(self.request.user)

Related

Access user object in decorator from request object for a logged in user

In my DRF app driven with APIView(), I want to add a single decorator. The decorator is:
from django.core.exceptions import PermissionDenied
from payment.models import Purchase
def client_has_paid(function):
'''
Has the client paid or have active subscription fee?
'''
def wrap(request, *args, **kwargs):
iccd = request.user.user_profile.iccd
filters = {'iccd': iccd , 'active': 1 }
try:
purchase = Purchase.objects.get(**filters)
return function(request, *args, **kwargs)
except:
raise PermissionDenied
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap
The error is in line request.user.user_profile.iccd which states user_profile don't exist (it does exist). doing
print(request.user)
gives out AnnonymousUser
Without the decorator, the API does print the correct user information as long as the passed token is valid.
The API that uses it is:
#method_decorator(client_has_paid, name='dispatch')
class AddIngredient(APIView):
permission_classes = [TokenHasReadWriteScope]
def post(self, request, cropped_land_id, format=None):
You can directly create drf style permission class and use it in your decorator, which would be more convenient. Just try this:
from rest_framework import permissions
class CustomPermission(permissions.BasePermission):
def has_permission(self, request, view):
iccd = request.user.user_profile.iccd
filters = {'iccd': iccd , 'active': 1 }
try:
purchase = Purchase.objects.get(**filters)
return True
except:
raise False
and use it in your view like:
class AddIngredient(APIView):
permission_classes = [CustomPermission]
def post(self, request, cropped_land_id, format=None):

Serializer `validate()` not getting called on `is_valid()` on POST

I want to create a non class-based form to facilitate uniform logging in by users across all front-end apps. Currently, it looks like this
My serializer class:
class EmployeeLoginSerializer(serializers.Serializer):
username = serializers.CharField(min_length=6)
password = serializers.CharField(min_length=6)
def validate_credentials(self, attrs):
print('validating')
try:
emp: Employee = Employee.objects.get(username=attrs['username'])
if crypto.verify_password(attrs['password'], emp.password_hash):
return attrs
except Exception:
raise serializers.ValidationError("Incorrect username or password")
raise serializers.ValidationError('Incorrect username or password')
My view class:
class TestView(APIView):
serializer_class = EmployeeLoginSerializer
def get(self, request, *args, **kwargs):
return Response({'Message': 'Get works'})
def post(self, request, *args, **kwargs):
print(request.POST)
serializer = self.serializer_class(data=request.POST)
if serializer.is_valid():
return Response({'Message': 'Credentials were correct'})
My issue is that serializer.is_valid() doesn't seem to be calling on validate automatically. I know I could just call serializer.validate() manually but all the docs and questions on StackOverflow seem to show validate() being called by is_valid() automatically so I get that feeling that that wouldn't be the best practice. Is there something I'm missing?
The is_valid() method will call validate() method of the serializer and validate_FIELD_NAME() methods.
In your code, the validate_credentials() seems a regular class method which won't detect by DRF since the credentials isn't a field on the serializer.

How to connect a renderer to a specific endpoint in a viewset, and only that endpoint

The DRF documentation shows how to connect a renderer to an APIView, but not how to do it for a specific action in a ViewSet. Given:
class XViewSet(ViewSet):
serializer_class = XSerializer
#action(detail=True, methods=['get'])
def my_action(self, request, pk=None):
..
How do I set a specific renderer for my_action, that will not affect the other/default actions in the viewset?
I can make an APIView just for that action of course but that makes for a more messy urls.py
As far as I can tell, the action takes any argument that can be a class attribute:
class XViewSet(ViewSet):
serializer_class = XSerializer
#action(detail=True, methods=['get'], renderer_classes=[yourrenderer])
def my_action(self, request, pk=None):
..

django rest framework find url kwarg in APIView

I have a url that looks like this:
url(r'^client_profile/address/(?P<id>.+)/$', views.ClientProfileAddressView.as_view())
And an APIView:
class ClientProfileAddressView(APIView):
renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,)
def put(self, request):
....
def get(self, request):
....
In both put and get, I need to access the id url kwarg, the first one to update the object, the second to update it. How can I access the url argument in those methods?
This should work:
def put(self, request, *args, **kwargs):
id = kwargs.get('id', 'Default Value if not there')
def get(self, request, *args, **kwargs):
id = kwargs.get('id', 'Default Value if not there')

Django Rest Framework - separate serializer class per method in model based API view

Say I have a simple Django REST Framework view that's extending multiple model classes and serves all the methods in one URL endpoint:
class UserAPIView(RetrieveAPIView, DestroyAPIView, BaseObjectAPIView):
permission_classes = (IsAuthenticated, )
serializer_class = UserSerializer
def get_serializer_class(self, *args, **kwargs):
# return different serializer depending on method??
# return UserUpdateSerializer
return UserViewSerializer
def get(self, request, *args, **kwargs):
"""
Retrieve user details
"""
# ...
return Response(data={'result': "OK"}, status=200)
def delete(self, request, pk):
"""
Delete user
"""
# ...
return Response(data={'result': "OK"}, status=200)
def put(self, request, pk):
"""
Change user
"""
# ...
return Response(data={'result': "OK"}, status=200)
Now I need to use different serializers per method, as my get-method will use different fields than my put-method, example serializers:
class UserViewSerializer(serializers.ModelSerializer):
firstname = serializers.Field(source='firstname')
lastname = serializers.Field(source='lastname')
username = serializers.Field(source='username')
class Meta:
model = User
class UserUpdateSerializer(serializers.ModelSerializer):
firstname = serializers.Field(source='firstname')
lastname = serializers.Field(source='lastname')
class Meta:
model = User
Is it possible to use different serializers for each method in my model based API view?
UPDATE:
I know how to use different serializers inside the methods themselves.
But I need to get the Browsable API generated by Swagger (Django module rest_framework_swagger) to retrieve different serializers for each method.
I can see that loading the API browser page triggers get_serializer_class, but inside that method, I don't know what method Swagger tries to get the serializer for.
How can I get rest_framework_swagger to retrieve different serializers per method?
I think there are at least two ways to achieve this:
You simply set the serializer that you want in each of your methods. Like this:
def get(self, request, *args, **kwargs):
self.serializer_class = UserViewSerializer
# ...
return Response(data={'result': "OK"}, status=200)
You override the get_Serializer_class method. Like this:
def get_serializer_class(self, *args, **kwargs):
if self.request.method == 'POST':
return UserUpdateSerializer
return UserViewSerializer
Hope this helps.
I suppose you could use yaml docstring on each method to override serializers. Like:
def put(self, request, pk):
"""Change user
---
serializer: .serializers.UserUpdateSerializer
"""
# ...
return Response(data={'result': "OK"}, status=200)

Resources