Django rest framework APIView Pagination EmptyPage - django

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!

Related

How to manually instantiate a SearchFilter in Django Rest Framework?

I have the following view but the search filter is not being applied. What am I missing?
class ListMyModelView(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
serializer_class = MyModelSerializer
pagination_class = StandardResultsSetPagination
filter_backends = (filters.SearchFilter,)
search_fields = ('field1', 'field2')
def get_queryset(self):
results = MyModel.objects.all()
return results.order_by('-last_modified').distinct()
def get(self, request):
paginator = self.pagination_class()
queryset = self.get_queryset()
results_page = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(results_page, many=True)
return paginator.get_paginated_response(serializer.data)

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
)

Range Filter not working in django rest does not react at all when filtered

Range Filter not working in django rest does not react at all when filtered
View
class MDShopListView(generics.ListAPIView):
queryset = smartphone.objects.all()
filter_backends = (DjangoFilterBackend,)
filterset_class = ShoppFilter
def get(self,request):
queryset = self.get_queryset()
serializer=MDShopListSerializer(queryset,many=True)
return Response(serializer.data)
You need to filter the queryset, with self.filter_queryset(…) [drf-doc]:
class MDShopListView(generics.ListAPIView):
queryset = smartphone.objects.all()
filter_backends = (DjangoFilterBackend,)
filterset_class = ShoppFilter
def get(self, request):
queryset = self.filter_queryset(self.get_queryset())
serializer=MDShopListSerializer(queryset,many=True)
return Response(serializer.data)
But actually overriding get(…) is not necessary, since now it does almost exactly what a ListAPIView does by default, except that you do not paginate. Indeed, a ListAPIView is defined as [GitHub]:
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
and the .list(…) method is defined in the ListModelMixin as [GitHub]:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
You thus can define this as:
class MDShopListView(generics.ListAPIView):
queryset = smartphone.objects.all()
filter_backends = (DjangoFilterBackend,)
filterset_class = ShoppFilter
# specify the serializer ↓
serializer_class = MDShopListSerializer

DestroyAPIView Django rest validation

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)

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