Django Rest Framework pagination on GenericViewSet - python

I have the following GenericViewSet, I am trying to achieve pagination for the viewset, This is my viewset
class UserAccountViewSet(viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin):
queryset = UserAccount.objects.all()
lookup_field = 'username'
lookup_url_kwarg = "username"
serializer_class = UserAccountSerializer
page_size = 25
page_size_query_param = 'page_size'
max_page_size = 1000
def list(self, request):
queryset = self.queryset
if request.GET.dict():
return Response(status=status.HTTP_501_NOT_IMPLEMENTED)
serializer = UserListSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, **kwargs):
pass
def create(self, request, *args, **kwargs):
pass
def update(self, request, *args, **kwargs):
pass
def destroy(self, request, *args, **kwargs):
pass
this is my configuration,
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '100/day'
}
}
It is not getting paginated, how can I make pagination work with DRF?
thank you.

Since you are overriding list() method and not returning a paginated response, you are not getting paginated response in your API. Instead, you should call super() in list() method as DRF's list() method itself returns a paginated response for generic views or viewsets.
Pagination is only performed automatically if you're using the generic
views or viewsets. If you're using a regular APIView, you'll need to
call into the pagination API yourself to ensure you return a paginated
response.
class UserAccountViewSet(viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin):
def list(self, request, *args, **kwargs):
if request.GET.dict():
return Response(status=status.HTTP_501_NOT_IMPLEMENTED)
# call 'super()' to get the paginated response
return super(UserAccountViewSet, self).list(request, *args, **kwargs)

Related

Override RetrieveUpdateAPIView Django Rest Framework

Right now I'm overriding the whole retrieve and update function. I want to override only that part, it does not ask for the pk value. Thanks
View.py
class EmployeeView(generics.RetrieveUpdateAPIView):
permission_classes = [EmployeePermission]
serializer_class = EmployeeSerializers
def retrieve(self, request, *args, **kwargs):
employee = Employee.objects.get(user=self.request.user)
serializer = EmployeeSerializers(employee)
return Response(serializer.data)
def update(self, request, *args, **kwargs):
employee_user = Employee.objects.get(user=self.request.user)
serializer = EmployeeSerializers(employee_user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)strong text
urls.py
path('viewEmployee/', views.EmployeeView.as_view()),
what you need is to override the get_queryset method, and write your custom filtering, then django will take care of otherthings.
class EmployeeView(generics.RetrieveUpdateAPIView):
queryset = Employee.objects.all()
permission_classes = [EmployeePermission]
serializer_class = EmployeeSerializers
def get_queryset(self):
return super().get_queryset().filter(
user=self.request.user
)

Pagination for only one specific method of ModelViewSet Django Rest Framework

I have a below class
class Home(viewsets.ModelViewSet):
serializer_class = HomeSerializer
queryset = Home.objects.all()
#action(detail=False, methods=['get'])
def blog_data(self, request):
queryset = Blogs.objects.filter(identifier='blog')
serializer = BlogDataSerializer(data=queryset, many=True) #other serializer specific to this method
serializer.is_valid()
return Response(serializer.data) # need pagination here for this method only
def list(self, request):
....
return Response(serializer.data)
i have overwritten list method and i only want pagination in blog_data method to have pagination with page_size and page_number(page) to be given as query params.
example:
http://localhost:8000/home?page=1&page_size=5
how would i acheive it, i have read about pagination_class = HomePagination
but i dont want it to impact list method or any other method in this class, i only want pagination in my blog_data method
pagination.py is
class HomePagination(PageNumberPagination):
page_size_query_param = 'page_size'
def get_paginated_response(self, data):
response = {
'no_of_records': self.page.paginator.count,
'no_of_pages': self.page.paginator.num_pages,
'page_size': int(self.request.GET.get('page_size')),
'page_no': self.page.number,
'results': data
}
return Response(response, status=status.HTTP_200_OK)
I have managed to solve this problem by doing
class Home(viewsets.ModelViewSet):
serializer_class = HomeSerializer
queryset = Home.objects.all()
#action(detail=False, methods=['get'])
def blog_data(self, request):
self.pagination_class = HomePagination # added this
queryset = Blogs.objects.filter(identifier='blog')
page_data = self.paginate_queryset(queryset) #added this
serializer = BlogDataSerializer(data=queryset, many=True) #other serializer specific to this method
serializer.is_valid()
paginated_data = self.get_paginated_response(serializer.data) # added this
return Response(paginated_data) # need pagination here for this method only
def list(self, request):
....
return Response(serializer.data)
Hope someone will get help from this in future.

Refactoring views in Django REST framework

I am very new to Python and Django. I have this app that returns 4 different types of transport routes (In the code I only showed two, cause they basically are the same...).
These 4 views use the same class-based views, but only the models' names are different. As they all return the same functionality(get, post, put and delete) I ended up repeating the same code over and over again.
Is there any way I can refactor it simpler?
Any help is appreciated! Thank you :)
views.py
********* tube view ***********
class TubeListView(APIView):
def get(self, _request, format=None):
tubeRoutes = TubeRoute.objects.all()
serialized_with_user = NestedTubeRouteSerializer(tubeRoutes, many=True)
return Response(serialized_with_user.data)
def post(self, request, format=None):
request.data['traveler'] = request.user.id
serializer = TubeRouteSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
class TubeDetailView(APIView):
def get(self, _request, pk, format=None):
tubeRoute = TubeRoute.objects.get(pk=pk)
serialized_with_user = NestedTubeRouteSerializer(tubeRoute)
return Response(serialized_with_user.data)
def put(self, request, pk, format=None):
request.data['traveler'] = request.user.id
tubeRoute = self.get_object(pk)
if tubeRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
updated_serializer = TubeRouteSerializer(tubeRoute)
if updated_serializer.is_valid():
updated_serializer.save()
return Response(updated_serializer.data, status=status.HTTP_200_OK)
return Response(updated_serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
def delete(self, request, pk, format=None):
tubeRoute = self.get_object(pk)
if tubeRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
tubeRoute.delete()
return Response(status=status.HTTP_200_OK)
********* bus view ***********
class BusListView(APIView):
def get(self, _request, format=None):
busRoutes = BusRoute.objects.all()
serialized_with_user = NestedBusRouteSerializer(busRoutes, many=True)
return Response(serialized_with_user.data)
def post(self, request, format=None):
request.data['traveler'] = request.user.id
serializer = BusRouteSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
class BusDetailView(APIView):
def get(self, _request, pk, format=None):
busRoute = BusRoute.objects.get(pk=pk)
serialized_with_user = NestedBusRouteSerializer(busRoute)
return Response(serialized_with_user.data)
def put(self, request, pk, format=None):
request.data['traveler'] = request.user.id
busRoute = self.get_object(pk)
if busRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
updated_serializer = BusRouteSerializer(busRoute)
if updated_serializer.is_valid():
updated_serializer.save()
return Response(updated_serializer.data, status=status.HTTP_200_OK)
return Response(updated_serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
def delete(self, request, pk, format=None):
busRoute = self.get_object(pk)
if busRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
busRoute.delete()
return Response(status=status.HTTP_200_OK)
You should take a look to these class based views in DRF.
For instance, the following code should be enough to replace your first TubeListView:
from rest_framework import generics
class TubeListView(generics.ListCreateAPIView):
queryset = TubeRoute.objects.all()
serializer_class = NestedTubeRouteSerializer
def post(self, request, *args, **kwargs):
request.data['traveler'] = request.user.id
return super().post(self, request, *args, **kwargs)
If you don't need any special behavior, you don't have to redefine get, post, etc methods. But if you need to change data, for instance in your POST method, you can do your stuff and then call the usual behavior of the superclass with super().post(self, request, *args, **kwargs)
I would recommend checking Viewsets in Django Rest Framework, specifically ModelViewset.
The actions provided by the ModelViewSet class are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy() which coincide with the following:
get ----> .retrieve()
list ----> .list()
post ----> .create()
patch ----> .partial_update()
put ----> .update()
delete ----> .destroy()
Let me provide a case using the Bus Views
The Modelviewset form would be:
class BusViewset(ModelViewset):
queryset = BusRoute.objects.all()
serializer_class = NestedBusRouteSerializer
def get_queryset(self):
return self.queryset.filter(
owner__id=self.request.user.id
)
For the permission checks you implemented, Django Rest Framework has a Permissions System that takes care of that. For you own use case, a custom permission would be sufficient.

Django REST Framework ApiView not allowing DELETE

I am new to Django and I am having a issue sending a DELETE request to a class based view which inherits from APIView.
Here is my class:
class PageDetail(APIView):
def get(self, request, pk):
page = get_object_or_404(Page, pk=pk)
data = PageSerializer(page).data
return Response(data)
def put(self, request, pk):
stream = io.BytesIO(request.body)
response = JSONParser().parse(stream)
page = Page.objects.get(pk=pk)
page.title = response.get('title', page.title)
page.content = response.get('content', page.content)
user = User.objects.get(pk=response.get('author', page.author))
page.author = user
page.save()
return HttpResponseRedirect(reverse('pages_detail', args=(page.id,)))
def delete(self, request, pk):
page = Page.objects.get(pk=pk)
page.delete()
return HttpResponseRedirect(reverse('pages_list'))
When I make the DELETE request, the resource is deleted, but the page responds with the message:
{'detail':'Method 'DELETE' not allowed.'}
Although in the header I have:
Allow: GET, PUT, DELETE, HEAD, OPTIONS
Anyone have any ideas?
Using APIView you need to specify the http_method_names attribute explicitly.
Example:
class PageDetail(APIView):
"""
page detail api view.
"""
permission_classes = (
permissions.IsAuthenticated,
)
http_method_names = [
'delete',
'head',
'options',
]
def delete(self, request, pk, *args, **kwargs):
return Response(status=200)

Paginator is not showing appropriate result

I want to show only 5 results and next 5 in other page.
my view.py
class QuestionList(APIView):
def get(self, request, *args, **kwargs):
res = Question.objects.all()
paginator = Paginator(res, 5)
serializer = QuestionSerializers(res, many=True)
return Response({"Section": serializer.data})
how will my paginator work here ?
Django-rest provide its own method of pagination. You should use that for instead of building your own. You can find its docs here.
You just have to add following settings in your settings.
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
Data in ListApiView will be paginated with this. In your current view
class QuestionList(APIView):
def get(self, request, *args, **kwargs):
res = Question.objects.all()
page = self.paginate_queryset(res)
serialized = QuestionSerializers(page, many=True)
return self.get_paginated_response(serialized.data)

Categories

Resources