I'd like to add pagination to my JsonResponse.
I'm currently using the django.http.JsonResponse
to generate json from an elastic search API. I'd like to add a pagination feature to be included. My code is as follows:
class ResultQueryView(View):
def get(self, request):
resource_meta = request.GET.getlist("resource_meta")
locations = request.GET.getlist("location")
page = request.GET.get("page")
logger.info("Got search query where resource_meta: {} and locations: {}".format(resource_meta, locations))
results = resource_query(resource_meta, locations)
resource_ids = [r["_id"] for r in results['hits']['hits']]
resources = get_enriched_resources(request.user, Resource.objects.filter(internal_id__in=resource_ids))
serialized = ResourceSerializer(resources, many=True)
return JsonResponse({"resources": serialized.data})
Paginated JSON will usually have an object with links to the previous and next JSON pages. To get the previous page, you must send a request to the "prev" URL. To get to the next page, you must send a request to the "next" URL. This will deliver a new JSON with new results and new links for the next and previous pages.
Additionally, switching to keyset pagination will improve the performance of page lookups and make them work in constant time. Django makes it easy to alter its default configuration, giving you the power to build a performant solution for pagination in Django.
An HttpResponse subclass that helps to create a JSON-encoded response. It inherits most behavior from its superclass with some differences: Its default Content-Type header is set to application/json .
JsonResponse is an HttpResponse subclass that helps to create a JSON-encoded response. Its default Content-Type header is set to application/json. The first parameter, data , should be a dict instance.
Use Django's Paginator
.
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
class ViewPaginatorMixin(object):
min_limit = 1
max_limit = 10
def paginate(self, object_list, page=1, limit=10, **kwargs):
try:
page = int(page)
if page < 1:
page = 1
except (TypeError, ValueError):
page = 1
try:
limit = int(limit)
if limit < self.min_limit:
limit = self.min_limit
if limit > self.max_limit:
limit = self.max_limit
except (ValueError, TypeError):
limit = self.max_limit
paginator = Paginator(object_list, limit)
try:
objects = paginator.page(page)
except PageNotAnInteger:
objects = paginator.page(1)
except EmptyPage:
objects = paginator.page(paginator.num_pages)
data = {
'previous_page': objects.has_previous() and objects.previous_page_number() or None,
'next_page': objects.has_next() and objects.next_page_number() or None,
'data': list(objects)
}
return data
Now, use the ViewPaginatorMixin
to support pagination for View
class ResultQueryView(ViewPaginatorMixin, View):
def get(self, request):
// code
serialized = ResourceSerializer(resources, many=True)
return JsonResponse({"resources": self.paginate(serialized.data, page, limit)})
A simple solution could be to slice serialized.data just before building JsonResponse (and maybe even annotate the result with total n. of expected pages, that is math.ceil(len(serialized.data) / PAGE_SIZE)
):
PAGE_SIZE = 10
start = page * PAGE_SIZE
stop = min(start + PAGE_SIZE, len(serialized.data))
#return JsonResponse({"resources": serialized.data})
return JsonResponse({"resources": serialized.data[start:stop]})
Test:
class FakeSerialized(object):
def __init__(self):
self.data = list(range(0,35))
serialized = FakeSerialized()
print('All data:', serialized.data)
PAGE_SIZE = 10
for page in range(0, 5):
start = page * PAGE_SIZE
stop = min(start + PAGE_SIZE, len(serialized.data))
data = serialized.data[start:stop]
print('Page %d:' % page, data)
Result:
$ python3 ./paginate.py
All data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
Page 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Page 1: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Page 2: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
Page 3: [30, 31, 32, 33, 34]
Page 4: []
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With