Access user object in decorator from request object for a logged in user - django-rest-framework

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):

Related

How to update another field when using partial_update?

I'm using partial_updates on my user model, and I wish to change the is_active to True on the user model instance when a partial_update happens - even though is_active is not exposed to the endpoint. My class looks like this:
class UserInvitationUpdate(mixins.UpdateModelMixin, generics.GenericAPIView):
serializer_class = serializers.UserSerializer
queryset = User.objects.all()
def get(request, *args, **kwargs):
username = kwargs.get('username')
token = kwargs.get('token')
return activated_user(username, token)
def get_object(self):
username = self.kwargs.get('username')
user = User.objects.get(username=username)
return user
def put(self, request, *args, **kwargs):
username = self.kwargs.get('username')
token = self.kwargs.get('token')
if my_if_statement_is_true:
# TODO set user to active
# how do I set is_active = True for the user model instance?
return self.partial_update(request, *args, **kwargs)
You have multiple way to deal with that. You could either change your serializer .save() method and set manually the field is_active to true, or set it in the view by updating the perform_update() method of your view :
def perform_update(self, serializer):
serializer.save(is_active=True)
More info here

Set permissions on Graphene Relay Node and Connection fields

How can I require authentication/authorization on the tier Node field and allTiers Connection field query below?
# schema.py
class TierNode(DjangoObjectType):
class Meta:
model = Tier
filter_fields = []
interfaces = (graphene.relay.Node,)
class Query(graphene.ObjectType):
tier = relay.Node.Field(TierNode)
all_tiers = DjangoFilterConnectionField(TierNode)
You can define a resolver for those fields with auth decorator like so:
from graphql_jwt.decorators import login_required
class Query(graphene.ObjectType):
tier = relay.Node.Field(TierNode)
all_tiers = DjangoFilterConnectionField(TierNode)
#login_required
def resolve_tier(root, info, **kwargs):
# code for resolving here
This is just using the login_decorator that comes with graphql_jwt but it will work for your custom decorators too if you defined them.
Furthermore, this also works for when you're resolving a field for TierNode:
class TierNode(DjangoObjectType):
class Meta:
model = Tier
filter_fields = []
interfaces = (graphene.relay.Node,)
some_property = graphene.Field("types.SomePropertyType")
#login_required
def resolve_some_property(root, info, **kwargs):
# code for resolving here
You can define authorization or/and authentication decorator like this:
from functools import wraps
def authorize_required(role):
def decorator(func):
#wraps(func)
def wrapper(instance, info, *args, **kwargs):
current_user = info.context.user
if not current_user.is_authenticated:
raise Exception("Authentication credentials were not provided")
if not authorize(instance, current_user, role):
raise Exception(
f"{current_user} has no access to {instance} with required {role=}"
)
return func(instance, info, *args, **kwargs)
return wrapper
return decorator
def authorize(instance, user, role) -> bool:
# check if user can have access to instance
# if there is requirement to have certain role
And use it in schema definition:
class TierNode(DjangoObjectType):
class Meta:
model = Tier
filter_fields = []
interfaces = (graphene.relay.Node,)
class Query(graphene.ObjectType):
tier = relay.Node.Field(TierNode)
all_tiers = DjangoFilterConnectionField(TierNode)
#authorize_required('user')
def resolve_tier(self, info, **args):
# some resolve code
#authorize_required('admin')
def resolve_all_tiers(self, info, **args):
# some resolve code

Access current user from viewsets.ViewSet

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)

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