Why does Pagination not work on GenericAPIView? - django

views.py
class variable__list(ListAPIView):
"""
get:
returns a list of variable names
"""
serializer_class = VariableSerializer
pagination_class = PageNumberPagination
page_size = 5
def get_queryset(self):
return Variable.objects.all()
def get(self, request, format=None):
# base queryset
queryset = self.get_queryset()
# return serialized data
if queryset.exists():
serializer = VariableSerializer(queryset, many=True)
return Response(serializer.data)
else:
return Response({"Returned empty queryset"}, status=status.HTTP_404_NOT_FOUND)
settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5,
}
When I go to the endpoint api/v1/variable/?page=1,
I get the same list returned of 100 results. My understanding is this should automatically be working when I set the pagination globally in settings.py, on top of that I have ALSO defined the paginator at the class level and still nothing is being paginated. What am I doing wrong here?

Remove get and get_queryset method, if there is no custom logic inside it.
class variable__list(ListAPIView):
"""
get:
returns a list of variable names
"""
queryset = Variable.objects.all()
serializer_class = VariableSerializer
pagination_class = PageNumberPagination
page_size = 5
If you have some custom code inside it, you need to send paginated response manually
def get(self, request, format=None)
paginator = PageNumberPagination()
paginator.page_size = 10
queryset = self.get_queryset()
result_page = paginator.paginate_queryset(queryset, request)
serializer = VariableSerializer(result_page, many=True)
return paginator.get_paginated_response(serializer.data)

Related

Django passing multiple ids through URL

For POST/GET (etc) requests I have the following URL for one user:
v1/users/userid123
registered as so:
router.register(r"v1/users", accounts_views_v1.UserViewSet)
What modifications should I make so that I can pass multiple user IDs like:
v1/users/?ids=["userid123","userid456"]?
It worked without any modification for another model of mine, with the same criteria, but this one keeps giving a 403 error (before even going into the method!) when I try it
Code per request:
My viewset is insanely long, here's the beginning though
class UserViewSet(MultipleDBModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.none()
#workspace_specific
def get_queryset(self):
group_ids = json.loads(self.request.query_params.get("group_id", "[]"))
queryset = None
if group_ids:
queryset = User.objects.filter(group_memberships__group__in=group_ids).distinct()
else:
queryset = User.objects.all()
return queryset.select_related("workspace_role").prefetch_related(
"group_memberships__group_role", "group_memberships__group"
)
and my URLS:
PREFIX = settings.REST_FRAMEWORK_ROUTER_PREFIX
if PREFIX:
PREFIX = r"^" + str(PREFIX) + r"/"
router = BulkRouter()
single_object_router = SingleObjectRouter()
lazy_single_object_create_or_update_router = LazySingleObjectCreateOrUpdateRouter()
...
router.register(r"v1/users", accounts_views_v1.UserViewSet)
...
urlpatterns = [...]
GET method:
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, exclude=None)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True, exclude=None)
return Response(serializer.data)
I found the kink, this function wraps all the others:
def permission_check_wrapper(self, request, *args, pk=None, **kwargs)
of course, when there are IDs instead of one pk, it doesn't work - how can I pass ids instead?

Pagination DRF not working well

I have a little problem with pagination when I put the default pagination in my project, somethings pages work in others pages not working for example:
this is my file settings.py for all my project
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apicolonybit.notification_clbt.NotificationPagination.PaginationList'}
this is my Configuration app inside my project: myproject / Configuration
class ConfigurationsList(generics.ListAPIView):
"""
list configuration with current user authenticated.
"""
queryset = Configuration.objects.all()
serializer_class = ConfigurationSerializer
when I run this part in my postman, working well, but when I try to run in another module like this:
class TransactionListHistory(generics.ListAPIView):
# queryset = TransactionHistory.objects.all()
serializer_class = TransactionHistorySerializer
pagination_class = PaginationList
page_size = 2
page = 1
def get_object(self, current_user):
# User.objects.get(id=pk)
return TransactionHistory.objects.filter(agent_id=current_user.id).order_by('-id')
#classmethod
def get_object_client(cls, current_user, trans):
# User.objects.get(id=pk)
return TransactionHistory.objects.filter(user_id=current_user.id).order_by('-id')
def get(self, request, format=None):
current_user = request.user
status_trans = 6
agent_user = 2
client_user = 1
trans = {
'status': status_trans
}
typeusers = Profile.objects.get(user_id=current_user.id)
# actions agent user = show all transaction from all client users
if typeusers.type_user == agent_user:
list_trans_init = self.get_object(current_user)
serializer = TransactionHistorySerializer(list_trans_init, many=True)
get_data = serializer.data
# actions normal user (client user) = just see transactions from self user
if typeusers.type_user == client_user:
list_trans_init = self.get_object_client(current_user, trans)
serializer = TransactionHistorySerializer(list_trans_init, many=True)
get_data = serializer.data
# if not PaginationList.get_next_link(self):
# return JsonResponse({'data': get_data}, safe=False, status=status.HTTP_200_OK)
return self.get_paginated_response(get_data)
my custom file pagination like this
class PaginationList(PageNumberPagination):
page_size = 2 # when show me an error I added
offset = 1 # when show me an error I added
limit = 10 # when show me an error I added
count = 10 # when show me an error I added
page = 1 # when show me an error I added
def get_paginated_response(self, data):
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'results': data
})
the variables page_size and etc, then show me an error like PaginationList is not object page, I added this page_size and pass other error like PaginationList is not object offset and again added var.
well the last error show me is like this 'int' object has no attribute 'has_next'
please help me, how to add my custom pagination in my class TransactionListHistory
thanks for your attention.
You are using self.get_paginated_response() in wrong way.
from rest_framework.response import Response
class TransactionListHistory(generics.ListAPIView):
# Your code
def get(self, request, *args, **kwargs):
queryset = do_something_and_return_QuerySet() # do some logical things here and
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)
the do_something_and_return_QuerySet() is fucntion or logic of you, which return a QuerySet.
Example
class TransactionListHistory(generics.ListAPIView):
# Your code
def get(self, request, *args, **kwargs):
queryset = TransactionHistory.objects.filter(user_id=request.user.id)
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)

How do you paginate a viewset using a paginator class?

According to the documentation:
Pagination is only performed automatically if you're using the generic views or viewsets
But this doesn't seem to be the case. Here's what I have for my viewset:
views.py
class EntityViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Entity.objects.all()
serializer = EntitySerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = Entity.objects.all()
entity = get_object_or_404(queryset, pk=pk)
serializer = EntitySerializer(entity)
return Response(serializer.data)
Here's my urls
entity_list = views.EntityViewSet.as_view({'get':'list'})
entity_detail = views.EntityViewSet.as_view({'get':'retrieve'})
...
url(r'^entity/$', entity_list, name='entity-list'),
url(r'^entity/(?P<pk>[0-9]+)/$', entity_detail, name='entity-detail'),
...
This is my pagination class
class PagePaginationWithTotalPages(pagination.PageNumberPagination):
page_size = 30
page_size_query_param = 'page_size'
max_page_size = 1000
def get_paginated_response(self, data):
return Response({
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'count': self.page.paginator.count,
'total_pages': self.page.paginator.num_pages,
'results': data
})
and I set it in settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
],
'DEFAULT_PAGINATION_CLASS': 'myapp.pagination.PagePaginationWithTotalPages',
'UNICODE_JSON': False,
}
Now while this works for ListAPIView it doesn't appear to work for my viewset. Is there a step that I'm missing?
For reference this works fine:
class EntitiesView(ListAPIView):
serializer_class = EntitySerializer
def get_queryset(self):
parameters = get_request_params(self.request)
qs = qs.filter(**parameters).distinct()
return qs
EDIT:
Changing it to use ModelViewset appears to have done the trick
class EntityViewSet(viewsets.ModelViewSet):
queryset = Entity.objects.all()
serializer_class = EntitySerializer
def list(self, request):
queryset = self.queryset
parameters = get_request_params(self.request)
if 'ordering' in parameters:
queryset = queryset.order_by(parameters['ordering'])
del parameters['ordering']
queryset = queryset.filter(**parameters).distinct()
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)
def retrieve(self, request, pk=None):
entity = self.get_object()
serializer = EntitySerializer(entity)
return Response(serializer.data)
Pagination is only performed automatically if you're using the generic views or viewsets
You seem to have missed the next sentence from the documentation:
If you're using a regular APIView, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the mixins.ListModelMixin and generics.GenericAPIView classes for an example.
As pointed by the documentation, the ListModelMixin will show you that you didn't call the paginate_queryset / get_paginated_response and thus did bypass the pagination as well as the filtering.
As pointed in the comments, you should consider ModelViewSet and define the required queryset to get it automatically included.

How do get custom pagination class working for django rest framework v3.6

I'm trying to implement a custom pagination class on a ViewSet, as per the docs, but the pagination settings are just not doing a single thing. Here's the code for my ViewSet.
from rest_framework import status, permissions, viewsets
from rest_framework.pagination import PageNumberPagination
class ProductViewSetPagination(PageNumberPagination):
page_size = 5
page_size_query_param = 'page_size'
max_page_size = 1000
class ProductViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = ProductSerializer
pagination_class = ProductViewSetPagination
# ...
def list(self, request):
#get_queryset is also overridden to accept filters in query_params
queryset = self.get_queryset()
if not queryset.exists():
return Response(status=status.HTTP_204_NO_CONTENT)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
I even added some defaults to the settings.py file, but I'm still getting all the product instances on a single page on the product-list view. I've tried adding page and page_size query parameters to the URL; this doesn't change anything.
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
What am I missing?
The pagination added in the super list, so try:
def list(self, request):
#get_queryset is also overridden to accept filters in query_params
queryset = self.get_queryset()
if not queryset.exists():
return Response(status=status.HTTP_204_NO_CONTENT)
return super(ProductViewSet, self).list(request)

Django filter not working

my filter isn't working Whenever I access http://localhost:8080/payables/invoices/?status=NOT_PAID It just returns all the invoices. I have no runtime error, the parameter I enter simply seems to be ignored. I really don't understand, other than that, it works well.
views.py
class InvoiceViewSet(viewsets.ViewSet):
serializer_class = InvoiceSerializer
filter_backend = filters.DjangoFilterBackend
filter_fields = ('status','supplier',)
def list(self,request,):
queryset = Invoice.objects.filter()
serializer = InvoiceSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = Invoice.objects.filter()
invoice = get_object_or_404(queryset, pk=pk)
serializer = InvoiceSerializer(invoice)
return Response(serializer.data)
class InvoiceItemViewSet(viewsets.ViewSet):
serializer_class = InvoiceItemSerializer
def list(self,request,invoice_pk=None):
queryset = InvoiceItem.objects.filter(invoice=invoice_pk)
serializer = InvoiceItemSerializer(queryset,many=True)
return Response(serializer.data)
def retrieve(self,request,pk,invoice_pk):
queryset = InvoiceItem.objects.filter(pk=pk,invoice=invoice_pk)
invoice_item = get_object_or_404(queryset,pk=pk)
serializer = InvoiceItemSerializer(invoice_item)
return Response(serializer.data)
url.py
from django.conf.urls import url, include
#viewset
from rest_framework_nested import routers
from payables.views import InvoiceViewSet,InvoiceItemViewSet
router = routers.SimpleRouter()
router.register(r'invoices', InvoiceViewSet,base_name='invoices')
invoice_item_router = routers.NestedSimpleRouter(router,r'invoices',lookup='invoice')
invoice_item_router.register(r'items', InvoiceItemViewSet, base_name='invoice_items')
urlpatterns = [
url(r'^',include(router.urls)),
url(r'^',include(invoice_item_router.urls))
]
It is because you are explicitly creating the queryset and hence the filter backend is never used:
queryset = Invoice.objects.filter()
I suggest looking at ModelViewSet. In that case you just have to pass queryset at the view level and rest will be taken care of.
instead of queryset = Invoice.objects.filter()
with queryset = self.filter_queryset(self.get_queryset()).filter()
instead of queryset = Invoice.objects.filter()
use queryset = self.get_queryset()
self.get_queryset() returns the filtered object list