mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-02 11:30:12 +03:00
Add Link and Off&lim pagination tests
This commit is contained in:
parent
00dc59c289
commit
ae73a191f9
|
@ -5,7 +5,8 @@ from django.db import models
|
|||
from django.core.paginator import Paginator
|
||||
from django.test import TestCase
|
||||
from django.utils import unittest
|
||||
from rest_framework import generics, status, pagination, filters, serializers
|
||||
from rest_framework import (generics, status, pagination, filters, serializers,
|
||||
mixins)
|
||||
from rest_framework.compat import django_filters
|
||||
from rest_framework.test import APIRequestFactory
|
||||
from rest_framework.tests.models import BasicModel
|
||||
|
@ -13,6 +14,48 @@ from rest_framework.tests.models import BasicModel
|
|||
factory = APIRequestFactory()
|
||||
|
||||
|
||||
def parse_header_link(value):
|
||||
# Got it from python-requests code
|
||||
# https://github.com/kennethreitz/requests/blob/master/requests/utils.py#L467
|
||||
def parse_header_links(value):
|
||||
"""Return a dict of parsed link headers proxies.
|
||||
|
||||
i.e. Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg",<http://.../back.jpeg>; rel=back;type="image/jpeg"
|
||||
|
||||
"""
|
||||
|
||||
links = []
|
||||
|
||||
replace_chars = " '\""
|
||||
|
||||
for val in value.split(","):
|
||||
try:
|
||||
url, params = val.split(";", 1)
|
||||
except ValueError:
|
||||
url, params = val, ''
|
||||
|
||||
link = {}
|
||||
|
||||
link["url"] = url.strip("<> '\"")
|
||||
|
||||
for param in params.split(";"):
|
||||
try:
|
||||
key, value = param.split("=")
|
||||
except ValueError:
|
||||
break
|
||||
|
||||
link[key.strip(replace_chars)] = value.strip(replace_chars)
|
||||
|
||||
links.append(link)
|
||||
|
||||
return links
|
||||
links = parse_header_links(value)
|
||||
link_map = {}
|
||||
for link in links:
|
||||
link_map[link['rel']] = link['url']
|
||||
return link_map
|
||||
|
||||
|
||||
class FilterableItem(models.Model):
|
||||
text = models.CharField(max_length=100)
|
||||
decimal = models.DecimalField(max_digits=4, decimal_places=2)
|
||||
|
@ -52,6 +95,18 @@ class MaxPaginateByView(generics.ListAPIView):
|
|||
paginate_by_param = 'page_size'
|
||||
|
||||
|
||||
class LinkPaginationView(mixins.LinkPaginationMixin,
|
||||
generics.ListCreateAPIView):
|
||||
model = BasicModel
|
||||
paginate_by = 10
|
||||
|
||||
|
||||
class OffsetLimitPaginationView(mixins.OffsetLimitPaginationMixin,
|
||||
generics.ListCreateAPIView):
|
||||
model = BasicModel
|
||||
paginate_by = 10
|
||||
|
||||
|
||||
class IntegrationTestPagination(TestCase):
|
||||
"""
|
||||
Integration tests for paginated list views.
|
||||
|
@ -103,6 +158,102 @@ class IntegrationTestPagination(TestCase):
|
|||
self.assertNotEqual(response.data['previous'], None)
|
||||
|
||||
|
||||
class IntegrationTestLinkPagination(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create 26 BasicModel instances.
|
||||
"""
|
||||
for char in 'abcdefghijklmnopqrstuvwxyz':
|
||||
BasicModel(text=char * 3).save()
|
||||
self.objects = BasicModel.objects
|
||||
self.data = [
|
||||
{'id': obj.id, 'text': obj.text}
|
||||
for obj in self.objects.all()
|
||||
]
|
||||
self.view = LinkPaginationView.as_view()
|
||||
|
||||
def test_get_paginated_root_view(self):
|
||||
"""
|
||||
GET requests to paginated ListCreateAPIView should return paginated results.
|
||||
"""
|
||||
request = factory.get('/')
|
||||
# Note: Database queries are a `SELECT COUNT`, and `SELECT <fields>`
|
||||
with self.assertNumQueries(2):
|
||||
response = self.view(request).render()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, self.data[0:10])
|
||||
link_header = parse_header_link(response['Link'])
|
||||
self.assertTrue('next' in link_header)
|
||||
self.assertTrue('last' in link_header)
|
||||
self.assertFalse('previous' in link_header)
|
||||
self.assertFalse('first' in link_header)
|
||||
|
||||
request = factory.get(link_header['next'])
|
||||
with self.assertNumQueries(2):
|
||||
response = self.view(request).render()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, self.data[10:20])
|
||||
link_header = parse_header_link(response['Link'])
|
||||
self.assertTrue('next' in link_header)
|
||||
self.assertTrue('last' in link_header)
|
||||
self.assertTrue('previous' in link_header)
|
||||
self.assertTrue('first' in link_header)
|
||||
|
||||
request = factory.get(link_header['last'])
|
||||
with self.assertNumQueries(2):
|
||||
response = self.view(request).render()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, self.data[20:])
|
||||
link_header = parse_header_link(response['Link'])
|
||||
self.assertFalse('next' in link_header)
|
||||
self.assertFalse('last' in link_header)
|
||||
self.assertTrue('previous' in link_header)
|
||||
self.assertTrue('first' in link_header)
|
||||
|
||||
|
||||
class IntegrationTestOffsetLimitPagination(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create 26 BasicModel instances.
|
||||
"""
|
||||
for char in 'abcdefghijklmnopqrstuvwxyz':
|
||||
BasicModel(text=char * 3).save()
|
||||
self.objects = BasicModel.objects
|
||||
self.data = [
|
||||
{'id': obj.id, 'text': obj.text}
|
||||
for obj in self.objects.all()
|
||||
]
|
||||
self.view = OffsetLimitPaginationView.as_view()
|
||||
|
||||
def test_get_paginated_root_view(self):
|
||||
"""
|
||||
GET requests to paginated ListCreateAPIView should return paginated results.
|
||||
"""
|
||||
request = factory.get('/')
|
||||
# Note: Database queries are a `SELECT COUNT`, and `SELECT <fields>`
|
||||
with self.assertNumQueries(2):
|
||||
response = self.view(request).render()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['results'], self.data[0:10])
|
||||
self.assertEqual(response.data['count'], 26)
|
||||
|
||||
request = factory.get('/', {'offset': 10})
|
||||
with self.assertNumQueries(2):
|
||||
response = self.view(request).render()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['results'], self.data[10:20])
|
||||
self.assertEqual(response.data['count'], 26)
|
||||
|
||||
request = factory.get('/', {'offset': 20, 'limit': 3})
|
||||
with self.assertNumQueries(2):
|
||||
response = self.view(request).render()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['results'], self.data[20:23])
|
||||
self.assertEqual(response.data['count'], 26)
|
||||
|
||||
|
||||
class IntegrationTestPaginationAndFiltering(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -258,6 +409,63 @@ class UnitTestPagination(TestCase):
|
|||
self.assertEqual(serializer.context, results.context)
|
||||
|
||||
|
||||
class UnitTestLinkPagination(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.objects = range(10)
|
||||
paginator = Paginator(self.objects, 2)
|
||||
self.first_page = paginator.page(1)
|
||||
self.third_page = paginator.page(3)
|
||||
self.last_page = paginator.page(5)
|
||||
|
||||
def test_native_pagination(self):
|
||||
serializer = pagination.LinkPaginationSerializer(self.first_page)
|
||||
self.assertEqual(serializer.data['next'], '?page=2')
|
||||
self.assertEqual(serializer.data['previous'], None)
|
||||
self.assertEqual(serializer.data['first'], None)
|
||||
self.assertEqual(serializer.data['last'], '?page=5')
|
||||
|
||||
serializer = pagination.LinkPaginationSerializer(self.third_page)
|
||||
self.assertEqual(serializer.data['next'], '?page=4')
|
||||
self.assertEqual(serializer.data['previous'], '?page=2')
|
||||
self.assertEqual(serializer.data['first'], '?page=1')
|
||||
self.assertEqual(serializer.data['last'], '?page=5')
|
||||
|
||||
serializer = pagination.LinkPaginationSerializer(self.last_page)
|
||||
self.assertEqual(serializer.data['next'], None)
|
||||
self.assertEqual(serializer.data['previous'], '?page=4')
|
||||
self.assertEqual(serializer.data['first'], '?page=1')
|
||||
self.assertEqual(serializer.data['last'], None)
|
||||
|
||||
|
||||
class UnitTestOffsetLimitPagination(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.objects = range(10)
|
||||
self.first_page = pagination.OffsetLimitPage(
|
||||
self.objects, offset=0, limit=2)
|
||||
self.third_page = pagination.OffsetLimitPage(
|
||||
self.objects, offset=4, limit=2)
|
||||
self.last_page = pagination.OffsetLimitPage(
|
||||
self.objects, offset=8, limit=2)
|
||||
|
||||
def test_native_pagination(self):
|
||||
serializer = pagination.OffsetLimitPaginationSerializer(
|
||||
self.first_page)
|
||||
self.assertEqual(serializer.data['count'], 10)
|
||||
self.assertEqual(serializer.data['results'], self.objects[:2])
|
||||
|
||||
serializer = pagination.OffsetLimitPaginationSerializer(
|
||||
self.third_page)
|
||||
self.assertEqual(serializer.data['count'], 10)
|
||||
self.assertEqual(serializer.data['results'], self.objects[4:6])
|
||||
|
||||
serializer = pagination.OffsetLimitPaginationSerializer(
|
||||
self.last_page)
|
||||
self.assertEqual(serializer.data['count'], 10)
|
||||
self.assertEqual(serializer.data['results'], self.objects[8:])
|
||||
|
||||
|
||||
class TestUnpaginated(TestCase):
|
||||
"""
|
||||
Tests for list views without pagination.
|
||||
|
|
Loading…
Reference in New Issue
Block a user