DestroyAPIView Django rest validation - django

class DeleteLedgerCategory(DestroyAPIView):
serializer_class = CategorySerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = Category.objects.filter(company = self.request.user.currently_activated_company, id=self.kwargs['pk'])
return queryset
def preform_destroy(self, instance):
if instance.is_default == True:
raise ValueError("Cannot delete default system category")
return instance.delete()
In above class based view. I need to add custom validation error message. ie. if instance.is_default == True: raise error... and only allow to delete the instance if no error encounters. If any unclear question. Do comment

Instead of just raise error you can customize response in destroy method:
from rest_framework.response import Response
class DeleteLedgerCategory(DestroyAPIView):
serializer_class = CategorySerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = Category.objects.filter(company = self.request.user.currently_activated_company, id=self.kwargs['pk'])
return queryset
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.is_default == True:
return Response("Cannot delete default system category", status=status.HTTP_400_BAD_REQUEST)
self.perform_destroy(instance)

You can use the destroy method here but you have to return a response whether it is successful or not
from rest_framework.response import Response
from rest_framework import status
class DeleteLedgerCategory(DestroyAPIView):
serializer_class = CategorySerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = Category.objects.filter(company = self.request.user.currently_activated_company, id=self.kwargs['pk'])
return queryset
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.is_default == True:
return Response("Cannot delete default system category", status=status.HTTP_403_FORBIDDEN)
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)

Related

Django rest framework APIView Pagination EmptyPage

Successfully made pagination work and here is my code:
from rest_framework import pagination
class CustomPagination(pagination.PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 10
page_query_param = 'p'
class PaginationHandlerMixin(object):
#property
def paginator(self):
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
else:
pass
return self._paginator
def paginate_queryset(self, queryset):
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
and this my view class:
class OrderOntrackViewPaginated(APIView, PaginationHandlerMixin):
queryset = Order.objects.filter(~Q(staged__iexact="offtrack")).values().order_by('-id')
serializer_class = OrderSerializer
pagination_class = CustomPagination
permission_classes = (permissions.AllowAny,)
http_method_names = ['get']
def get(self, request):
results = self.paginate_queryset(self.queryset)
serializer = self.serializer_class(results, many=True)
return self.get_paginated_response(serializer.data)
my question is if I can handle EmptyPage so if max page is for example 3 and user puts ...?p=4 in the request, I could return a valid response.
I think could be easy to use ListAPIView like this:
from rest_framework.generics import ListAPIView
class OrderOntrackViewPaginated(ListAPIView):
queryset = Order.objects.exclude(staged__iexact="offtrack").order_by('-id')
serializer_class = OrderSerializer
pagination_class = CustomPagination
permission_classes = (permissions.AllowAny,)
http_method_names = ['get']
I hope this help you. Regards!

How to Convert Api view class to CreateModelMixin

class Add_Product(APIView):
def post(self,request,*args, **kwargs):
user=request.user
if user.is_authenticated:
data=request.data
date=datetime.now().date()
slug=user.username+"-"f'{int(time())}'
print(data)
serializer=ProductSerializer(data=data,many=True)
if serializer.is_valid():
print(serializer.data)
serializer.save(user=request.user,slug=slug)
return Response("Your product is added")
return Response(serializer.errors)
return Response("Login First")
I want to convert this to CreateModelMixin But i don't know how to pass values like request.user and slug in create method.
class Product_List(GenericAPIView,CreateModelMixin):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def post(self,request,*args, **kwargs):
return self.create(request,*args,**kwargs)
You can pass the user to the serializer through its context then override its create method:
# View
class Product_List(GenericAPIView,CreateModelMixin):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = (IsAuthenticated,)
def get_serializer_context(self):
return {'user': self.request.user}
# Serializer
class ProductSerializer(serializers.ModelSerializer):
[...]
def create(self, validated_data):
user = self.context['user']
slug = f'{user.username}-{int(time())}'
return Product.objects.create(
user=user,
slug=slug,
**validated_data
)

DRF Custom Permission is not firing

I wrote a custom permission class for a drf project to protect my view:
views.py
class Employee(APIView):
permission_classes = [BelongsToClient]
serializer_class = EmployeeSerializer
def get(self, request, pk, format=None):
employee = EmployeeModel.objects.get(pk=pk)
serializer = EmployeeSerializer(employee, many=False)
return Response(serializer.data)
def delete(self, request, pk, format=None):
employee = EmployeeModel.objects.get(pk=pk)
employee.Employees_deleted = True
employee.save()
return Response(status=status.HTTP_200_OK)
My permission class:
permission.py
from rest_framework import permissions
class BelongsToClient(permissions.BasePermission):
message= "You are only authorized to view objects of your client"
"""
Object-level permission to only see objects of the authenticated users client
"""
def has_object_permission(self, request, view, obj):
if obj.Mandant == request.user.Mandant:
return True
else:
return False
Unfortunatly this permission class isn't blocking my view even when it should. I dont know why. Did I miss something?
You need to call check_object_permissions method before response for APIView
class Employee(APIView):
permission_classes = [BelongsToClient]
serializer_class = EmployeeSerializer
def get(self, request, pk, format=None):
employee = EmployeeModel.objects.get(pk=pk)
serializer = EmployeeSerializer(employee, many=False)
self.check_object_permissions(request, employee)
return Response(serializer.data)
has_object_permission only called when you use the DestroyAPIView or RetrieveAPIView or ViewSet.
Try to use a viewset just like below
from rest_framework import viewsets
class Employee(viewsets.ViewSet):
permission_classes = [BelongsToClient]
serializer_class = EmployeeSerializer
def delete(self, request, pk, format=None):
employee = EmployeeModel.objects.get(pk=pk)
self.check_object_permissions(request, employee)
employee.Employees_deleted = True
employee.save()
return Response(status=status.HTTP_200_OK)
Note: I didn't test it but it should work.

Possible to have multiple serializers per view using Django Rest Framework

As I'm working, my ./views.py is becoming really competitive and I'm wondering if there is a way to refactor it to be DRY. I think in order to do so, I would need the ability to use specify multiple serializers per view.
data would need to be less ambigious so that it actually describes the data it is receiving so that it can be passed into the correct serializer, which would mean it would need to know the API route the received data came from. Not sure how to do that except with a one class per route, how I currently have it setup.
Then there would need to be a way to specify multiple serializers in a view to send the respective data to. Not seeing that this is possible.
# ./urls.py
from .views import (
SecurityQuestionsAPIView,
UserSigninTokenAPIView,
UsernameRecoveryAPIView,
ValidateKeyAPIView
)
urlpatterns = [
url(r'^signin/', UserSigninTokenAPIView.as_view(), name='signin'),
url(r'^verify/', verify_jwt_token),
url(r'^refresh/', refresh_jwt_token),
url(r'^username_recovery/', UsernameRecoveryAPIView.as_view(), name='username_recovery'),
url(r'^validate_key/', ValidateKeyAPIView.as_view(), name='validate_key'),
url(r'^security_questions/', SecurityQuestionsAPIView.as_view(), name='security_questions'),
]
# ./views.py
from .serializers import (
SecurityQuestionsSerializer,
UserSigninTokenSerializer,
UsernameRecoverySerializer,
ValidateKeySerializer
)
# Used for logging into the web application
class UserSigninTokenAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = UserSigninTokenSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UserSigninTokenSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
class UsernameRecoveryAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = UsernameRecoverySerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UsernameRecoverySerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
class ValidateKeyAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = ValidateKeySerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = ValidateKeySerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
class SecurityQuestionsAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = SecurityQuestionsSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = SecurityQuestionsSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
You are likely looking for the CreateApiView
class UserSigninTokenAPIView(CreateApiView):
permission_classes = [AllowAny]
serializer_class = UserSigninTokenSerializer
class UsernameRecoveryAPIView(CreateApiView):
permission_classes = [AllowAny]
serializer_class = UsernameRecoverySerializer
class ValidateKeyAPIView(CreateApiView):
permission_classes = [AllowAny]
serializer_class = ValidateKeySerializer
class SecurityQuestionsAPIView(CreateApiView):
permission_classes = [AllowAny]
serializer_class = SecurityQuestionsSerializer

django rest framework - object permission does not trigger

I have the following setup:
permission.py
def get_permission_level(request, obj):
try:
level = PasswordListACL.objects.get(list=obj, user=request.user)
except PasswordListACL.DoesNotExist:
level = None
return level
class IsPasswordListOwner(permissions.DjangoObjectPermissions):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
print(get_permission_level(request, obj))
if get_permission_level(request, obj) == None:
print('false')
return False
else:
level = AccessLevel.objects.get(pk=get_permission_level(request, obj).level_id).name
if level == 'Owner':
return True
else:
return False
I can confirm through my print('false') that the permission is being acted on and it is returning the correct value (I'm expecting False) however the view is still returning the data instead of a 403.
views.py
class PasswordListViewSet(viewsets.ModelViewSet):
queryset = PasswordList.objects.all()
serializer_class = PasswordListSerializer
permission_classes = (IsPasswordListOwner,)
def list(self, request):
self.permission_classes = [IsPasswordListOwner, ]
queryset = self.get_queryset().filter(passwordlistacl__user=request.user)
serializer = PasswordListSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, pk=None):
self.permission_classes = [IsPasswordListOwner, ]
queryset = self.get_queryset()
if get_object_or_404(queryset, pk=pk):
password = get_object_or_404(queryset, pk=pk)
else:
pass
serializer = PasswordListSerializer(password, context={'request': request})
return Response(serializer.data)
edit - changing the return on has_permission confirms the permission there is overriding my has_object_permission?
Doing this gives me a 403 (correct):
def has_permission(self, request, view):
return False
The documentation states:
If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.
So since you're not calling get_object() you need to be calling self.check_object_permissions(self.request, obj) at some point.