How to Make use of Pagination in this API using Django - django

Here I am trying to create a getData API using Django Rest Framework in which i want to get data using Pagination, i had created this statically but it should be like (getting PAGE and number of ROWS on that page) in request and accordingly data get fetch from database and also show the number entries i got.
please help me out to solve this, i have no idea about how pagination works logically just have basic understanding.
class DeviceControlPolicyView(APIView):
def get(self, request):
if request.data.get('page', 'rows'):
if request.data.get('page') == "1" and request.data.get('rows') == "1":
print(request.data.get('rows'))
print(request.data.get('page'))
qry = DeviceControlPolicy.objects.all()[0:1]
serializer = DeviceControlPolicySerializer(qry, many=True).data
entries = 1
data = {
'details':serializer,
'entry':entries
}
return Response(data)
elif request.data.get('page') == "1" and request.data.get('rows') == "2":
print(request.data.get('rows'))
print(request.data.get('page'))
qry = DeviceControlPolicy.objects.all()[0:2]
serializer = DeviceControlPolicySerializer(qry, many=True).data
entries = 2
data = {
'details': serializer,
'entry': entries
}
return Response(data)

Have a look at DRF's pagination documentation here.
You can set the pagination class in the APIView and have DRF take care of paginating responses from the queryset.
In your case try
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
page_size = 100
page_size_query_param = 'rows'
max_page_size = 1000
class DeviceControlPolicyView(generics.ListAPIView):
queryset = DeviceControlPolicy.objects.all()
serializer_class = DeviceControlPolicySerializer
pagination_class = CustomPageNumberPagination
also refer DRF generics

Related

How can I improve pagination performance in multiple queryset django?

I have 2 queryset :
queryset_primary = PrimaryUserSerializer(FileUpload.objects.all().order_by('name'), many=True, context=context).data
queryset_secondary = MemberSerializer(Members.objects.all().order_by('member_name'), many=True, context=context).data
Both having different keys ..so that I iterated both querysets :
response = []
for primary in queryset_primary:
# name_pri=primary['primary_user_id']
new_data={
'user_id' : primary['primary_user_id'],
'name': primary['name'],
}
response.append(new_data)
for secondary in queryset_secondary:
new_data={
'user_id' : secondary['secondary_user_id'],
'name': secondary['member_name'],
}
Again I used a common serializer having similar keys in it, for pagination :
responses = self.paginate_queryset(response)
if responses is not None:
serializer = CommonUserSerializer(responses,many=True)
data = {
'code': 200,
'status': "OK",
}
page_nated_data = self.get_paginated_response(serializer.data).data
data.update(page_nated_data)
data['response'] = data.pop('results')
return Response(data)
It totally taking 8 seconds of loading time.
How can I reduce the API loading time ?
When exposing bulk data, models and serializers should be avoided where possible. Have a look at Django's ORM .values() option to make a specific select. Try to construct your own structures for json serialization. I'd also avoid embedding nested data in your structures (especially in list views). It might turn out much cheaper to have a client call twice or filter your api then returning 'complete' data at once.
Have you tried to profile the specific api call? Django's debug toolbar is a convenient tool to pin point the bottleneck.
Have a look at .values() in the ORM:
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#values
Members.objects.all().order_by('member_name').values()
This should give you a QuerySet that returns dictionaries, rather than model instances, when used as an iterable.
The you could couple this with:
from django.http import JsonResponse
def some_view(request):
data = list(Members.objects.all().order_by('member_name').values()) # Use list(), because QuerySet is not JSON serializable by default!
return JsonResponse(data, safe=False)
More specific to your question:
from rest_framework import status
from rest_framework.response import Response
def some_view(request):
merged = []
merged.append(
list(Members.objects.all().order_by('member_name').values('user_id'. 'name')) + list(FileUpload.objects.all().order_by('name').values('user_id'. 'name'))
)
responses = self.paginate_queryset(merged)
if responses is not None:
serializer = CommonUserSerializer(responses, many=True)
data = {
'success': true,
}
paginated_data = self.get_paginated_response(serializer.data).data
data.update(paginated_data)
data['response'] = data.pop('results')
return Response(data, status=status.HTTP_200_OK)
You will also need to do some annotations() to get your user_id attributes to primary_user_id and secondary_user_id
Much more efficient!

I cannot paginate with the ListModelMixin class an action decorator

Good night I can not paginate with the ListModelMixin class an action decorator, the question is that I page but I show on each page the total number of objects that are going to be displayed, that is, if I have 27 records, the 27 is shown on each page.
Page 1
Page 2
class PostPageNumberPagination(PageNumberPagination):
page_size = 20
class InterfacesViewSet(viewsets.ModelViewSet):
queryset = Interfaces.objects.all()
serializer_class = InterfaceSerializer
pagination_class = PostPageNumberPagination
def get_response_data(self,paginated_queryset):
data =[ {
'id_interface': interface.id_interface,
'id_EquipoOrigen': interface.id_EquipoOrigen_id,
'EquipoOrigen': interface.id_EquipoOrigen.nombre,
'LocalidadOrigen': interface.id_EquipoOrigen.localidad,
'CategoriaOrigen': interface.id_EquipoOrigen.categoria,
'id_PuertoOrigen': interface.id_PuertoOrigen_id,
'PuertoOrigen': interface.id_PuertoOrigen.nombre,
'estatus': interface.estatus,
'etiqueta_prtg': interface.etiqueta_prtg,
'grupo': interface.grupo,
'if_index': interface.if_index,
'bw': interface.bw,
'bw_al': interface.bw_al,
'id_prtg': interface.id_prtg,
'ospf': interface.ospf,
'description': interface.description,
'id_EquipoDestino': interface.id_EquipoDestino_id,
'EquipoDestino': interface.id_EquipoDestino.nombre,
'LocalidadDestino': interface.id_EquipoDestino.localidad,
'CategoriaDestino': interface.id_EquipoDestino.categoria,
'id_PuertoDestino': interface.id_PuertoDestino_id,
'PuertoDestino': interface.id_PuertoDestino.nombre,
'ultima_actualizacion': interface.ultima_actualizacion,
} for interface in self.queryset]
return data
#action(methods=['get'], detail=False, url_path='registros-data-table', url_name='registros_data_table')
# List ModelViewSet
def registros_data_table(self, request):
queryset = Interfaces.objects.all()
page = self.paginate_queryset(queryset)
if page is not None:
data = self.get_response_data(page)
return self.get_paginated_response(data)
data = self.get_response_data(queryset)
return Response(data)
Where are you using ListModelMixin? can you put the entired code where you're using it? or can you explain details of what are you trying to do?
I think what you are currently doing is complex without reason. According to Django rest framework pagination docs, it is simple to make pagination.
In your api.py file you need:
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 1000
And in each ViewSet, just add:
pagination_class = StandardResultsSetPagination

How to paginate response from function based view of django rest framework?

I have written a code snippet like below, i need to achieve pagination in this, kindly let me know how is it possible. Also due to some reasons i want to use only function based views.
#api_view(['GET'])
#permission_classes([AllowAny])
def PersonView(request):
context={'request': request}
person_objects = Person.objects.all()
if len(person_objects) > 0:
person_data = PersonSerializer(person_objects, many=True, context=context)
return Response(person_data.data, status=status.HTTP_200_OK)
else:
return Response({}, status=status.HTTP_200_OK)
http://www.django-rest-framework.org/api-guide/pagination/
from rest_framework.pagination import PageNumberPagination
#api_view(['GET',])
#permission_classes([AllowAny,])
def PersonView(request):
paginator = PageNumberPagination()
paginator.page_size = 10
person_objects = Person.objects.all()
result_page = paginator.paginate_queryset(person_objects, request)
serializer = PersonSerializer(result_page, many=True)
return paginator.get_paginated_response(serializer.data)
You can also define custom pagination class by overriding PageNumberPagination
pagination.py
from rest_framework import pagination
class StandardResultsSetPagination(pagination.PageNumberPagination):
page_size = 10
page_query_param = 'page'
page_size_query_param = 'per_page'
max_page_size = 1000
it will help to define
page_size, page query custom parameters and max_page_size
views.py
from rest_api.pagination import StandardResultsSetPagination
#api_view(['GET',])
#permission_classes([AllowAny,])
def PersonView(request):
person_objects = Person.objects.all()
if len(person_objects)> 0:
paginator = StandardResultsSetPagination()
result_page = paginator.paginate_queryset(person_objects, request)
serializer = PersonSerializer(result_page, many=True)
return paginator.get_paginated_response(serializer.data)
else:
return Response({},status=status.HTTP_200_OK)
Eg:
Request
GET https://api.example.org/persons/?page=1&per_page=10
Response
HTTP 200 OK
{
"count": 1023
"next": "https://api.example.org/persons/?page=2&per_page=10",
"previous": null,
"results": [
…
]
}
The ACCEPTED answer has a BUG
A ModelSerializer accepts a queryset or an object, in the accepted answer the PersonSerializer is given the ouput of paginator.paginate_queryset which returns a list containing single element of class Page and thus the serializer will either return incomplete data or no data at all(know this because have tried and seen wrong results).
This can be easily fixed by passing the actual queryset to the serializer which in this case will be person_objects,
So the final code will be,
from rest_framework.pagination import PageNumberPagination
#api_view(['GET',])
#permission_classes([AllowAny,])
def PersonView(request):
paginator = PageNumberPagination()
paginator.page_size = 10
person_objects = Person.objects.all()
result_page = paginator.paginate_queryset(person_objects, request)
serializer = PersonSerializer(person_objects, many=True) # MAIN CHANGE IS HERE
return paginator.get_paginated_response(serializer.data)
This fixes the bug but passing the complete queryset to serializer will take lot of time for serializing since serializer works on lazy approach, but this performance issue is in itself a new question.

How to paginate with filters in django rest framework

I currently have an API view setup as follows:
class WeatherObservationSerializer(serializers.ModelSerializer):
dew_point = serializers.Field(source='dew_point')
wind_gust = serializers.Field(source='get_wind_gust')
class Meta:
model = WeatherObservation
fields = ('id', 'station', 'temperature', 'pressure', 'humidity',
'wind_direction', 'wind_speed', 'rainfall', 'date',
'dew_point', 'wind_gust')
class WeatherObservationList(generics.ListCreateAPIView):
model = WeatherObservation
serializer_class = WeatherObservationSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
queryset = WeatherObservation.objects.all()
min_date = self.request.QUERY_PARAMS.get('min_date', None)
station = self.request.QUERY_PARAMS.get('station', None)
if min_date is not None:
queryset = queryset.filter(date__gte=min_date)
if station is not None:
queryset = queryset.filter(station=station)
return queryset
My settings.py contains:
REST_FRAMEWORK = {
'PAGINATE_BY': 50,
'PAGINATE_BY_PARAM': 'page'
}
When I make a request to the API like so: /api/weather/observations/?station=2&page=2&min_date=2013-3-14 I only get back two results. If it's for page 3, 3 results, and so on. Is there anything I'm doing wrong that is causing this problem?
Cheers.
Check out the docs for those settings:
PAGINATE_BY_PARAM
The name of a query parameter, which can be used by the client to overide the default page size to use for pagination. If set to None, clients may not override the default page size.
Simply remove that line from your settings.py and you should be fine.
UPDATE 1/7/2016:
Note that this setting is now in the process of deprecation. You can consult the pagination guide for more details.
The short version is that you should now create a custom Pagination class with the appropriate settings which you then apply to your view. The examples in linked guide should be more than helpful.
Not sure how helpful that would be, but I needed to paginate a single view in my project that uses filter. what i did was
class GlobalFilter(CustomMixin):
from rest_framework.pagination import PageNumberPagination
queryset = Data.objects.all()
filter_backends = [DjangoFilterBackend]
serializer_class = FilterSerializer
filterset_class = GlobalFilterSet
pagination_class = PageNumberPagination
pagination_class.page_size = 100
Could be helpful for someone else maybe :)
Use filter-and-pagination plugin. Is well handling Filter & Pagination feature
pip install filter-and-pagination

Adding root element to json response (django-rest-framework)

I am trying to determine the best way to add a root element to all json responses using django and django-rest-framework.
I think adding a custom renderer is the best way to accomplish what I want to achieve and this is what I have come up with so far:
from rest_framework.renderers import JSONRenderer
class CustomJSONRenderer(JSONRenderer):
#override the render method
def render(self, data, accepted_media_type=None, renderer_context=None):
#call super, as we really just want to mess with the data returned
json_str = super(CustomJSONRenderer, self).render(data, accepted_media_type, renderer_context)
root_element = 'contact'
#wrap the json string in the desired root element
ret = '{%s: %s}' % (root_element, json_str)
return ret
The tricky part now is dynamically setting the root_element based on the view that render() is being called from.
Any pointers/advice would be greatly appreciated,
Cheers
For posterity, below is the final solution. It has grown slightly from the original as it now reformats paginated results as well.
Also I should have specified before, that the reason for the JSON root element is for integration with an Ember front end solution.
serializer:
from rest_framework.serializers import ModelSerializer
from api.models import Contact
class ContactSerializer(ModelSerializer):
class Meta:
model = Contact
#define the resource we wish to use for the root element of the response
resource_name = 'contact'
fields = ('id', 'first_name', 'last_name', 'phone_number', 'company')
renderer:
from rest_framework.renderers import JSONRenderer
class CustomJSONRenderer(JSONRenderer):
"""
Override the render method of the django rest framework JSONRenderer to allow the following:
* adding a resource_name root element to all GET requests formatted with JSON
* reformatting paginated results to the following structure {meta: {}, resource_name: [{},{}]}
NB: This solution requires a custom pagination serializer and an attribute of 'resource_name'
defined in the serializer
"""
def render(self, data, accepted_media_type=None, renderer_context=None):
response_data = {}
#determine the resource name for this request - default to objects if not defined
resource = getattr(renderer_context.get('view').get_serializer().Meta, 'resource_name', 'objects')
#check if the results have been paginated
if data.get('paginated_results'):
#add the resource key and copy the results
response_data['meta'] = data.get('meta')
response_data[resource] = data.get('paginated_results')
else:
response_data[resource] = data
#call super to render the response
response = super(CustomJSONRenderer, self).render(response_data, accepted_media_type, renderer_context)
return response
pagination:
from rest_framework import pagination, serializers
class CustomMetaSerializer(serializers.Serializer):
next_page = pagination.NextPageField(source='*')
prev_page = pagination.PreviousPageField(source='*')
record_count = serializers.Field(source='paginator.count')
class CustomPaginationSerializer(pagination.BasePaginationSerializer):
# Takes the page object as the source
meta = CustomMetaSerializer(source='*')
results_field = 'paginated_results'
Credit to ever.wakeful for getting me 95% of the way there.
Personally, I wanted to add meta data to every api request for a certain object, regardless of whether or not it was paginated. I also wanted to simply pass in a dict object that I defined manually.
Tweaked Custom Renderer
class CustomJSONRenderer(renderers.JSONRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
response_data = {}
# Name the object list
object_list = 'results'
try:
meta_dict = getattr(renderer_context.get('view').get_serializer().Meta, 'meta_dict')
except:
meta_dict = dict()
try:
data.get('paginated_results')
response_data['meta'] = data['meta']
response_data[object_list] = data['results']
except:
response_data[object_list] = data
response_data['meta'] = dict()
# Add custom meta data
response_data['meta'].update(meta_dict)
# Call super to render the response
response = super(CustomJSONRenderer, self).render(response_data, accepted_media_type, renderer_context)
return response
Parent Serializer and View Example
class MovieListSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
meta_dict = dict()
meta_dict['foo'] = 'bar'
class MovieViewSet(generics.ListAPIView):
queryset = Movie.objects.exclude(image__exact = "")
serializer_class = MovieListSerializer
permission_classes = (IsAdminOrReadOnly,)
renderer_classes = (CustomJSONRenderer,)
pagination_serializer_class = CustomPaginationSerializer
paginate_by = 10