Skip to content

Commit a1866b3

Browse files
committed
Add test_ascending with nulls
1 parent e44d8de commit a1866b3

File tree

2 files changed

+78
-5
lines changed

2 files changed

+78
-5
lines changed

rest_framework/pagination.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,9 @@ def paginate_queryset(self, queryset, request, view=None):
633633
kwargs = {order_attr + '__gt': current_position}
634634

635635
# If some records contain a null for the ordering field, don't lose them.
636-
filter_query = Q(**kwargs) | Q(**{order_attr + '__isnull': True})
637-
636+
filter_query = Q(**kwargs)
637+
if reverse:
638+
filter_query |= Q(**{order_attr + '__isnull': True})
638639
queryset = queryset.filter(filter_query)
639640

640641
# If we have an offset cursor then offset the entire page by that amount.
@@ -708,7 +709,7 @@ def get_next_link(self):
708709
# The item in this position and the item following it
709710
# have different positions. We can use this position as
710711
# our marker.
711-
has_item_with_unique_position = True
712+
has_item_with_unique_position = position is not None
712713
break
713714

714715
# The item in this position has the same position as the item
@@ -761,7 +762,7 @@ def get_previous_link(self):
761762
# The item in this position and the item following it
762763
# have different positions. We can use this position as
763764
# our marker.
764-
has_item_with_unique_position = True
765+
has_item_with_unique_position = position is not None
765766
break
766767

767768
# The item in this position has the same position as the item
@@ -887,7 +888,7 @@ def _get_position_from_instance(self, instance, ordering):
887888
attr = instance[field_name]
888889
else:
889890
attr = getattr(instance, field_name)
890-
return str(attr)
891+
return None if attr is None else str(attr)
891892

892893
def get_paginated_response(self, data):
893894
return Response(OrderedDict([

tests/test_pagination.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,78 @@ def get_pages(self, url):
10841084
return (previous, current, next, previous_url, next_url)
10851085

10861086

1087+
class NullableCursorPaginationModel(models.Model):
1088+
created = models.IntegerField(null=True)
1089+
1090+
1091+
class TestCursorPaginationWithNulls(TestCase):
1092+
"""
1093+
Unit tests for `pagination.CursorPagination` with ordering on a nullable field.
1094+
"""
1095+
1096+
def setUp(self):
1097+
class ExamplePagination(pagination.CursorPagination):
1098+
page_size = 1
1099+
ordering = 'created'
1100+
1101+
self.pagination = ExamplePagination()
1102+
data = [
1103+
None, None, 3, 4
1104+
]
1105+
for idx in data:
1106+
NullableCursorPaginationModel.objects.create(created=idx)
1107+
1108+
self.queryset = NullableCursorPaginationModel.objects.all()
1109+
1110+
get_pages = TestCursorPagination.get_pages
1111+
1112+
def test_ascending(self):
1113+
(previous, current, next, previous_url, next_url) = self.get_pages('/')
1114+
1115+
assert previous is None
1116+
assert current == [None]
1117+
assert next == [None]
1118+
assert previous_url is None
1119+
1120+
(previous, current, next, previous_url, next_url) = self.get_pages(next_url)
1121+
1122+
assert previous == [None]
1123+
assert current == [None]
1124+
assert next == [3]
1125+
1126+
(previous, current, next, previous_url, next_url) = self.get_pages(next_url)
1127+
1128+
assert previous == [3] # [None] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L789
1129+
assert current == [3]
1130+
assert next == [4]
1131+
1132+
(previous, current, next, previous_url, next_url) = self.get_pages(next_url)
1133+
1134+
assert previous == [3]
1135+
assert current == [4]
1136+
assert next is None
1137+
assert next_url is None
1138+
1139+
(previous, current, next, previous_url, next_url) = self.get_pages(previous_url)
1140+
1141+
assert previous == [None]
1142+
assert current == [3]
1143+
assert next == [4]
1144+
1145+
(previous, current, next, previous_url, next_url) = self.get_pages(previous_url)
1146+
1147+
assert previous == [None]
1148+
assert current == [None]
1149+
assert next == [None] # [3] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L731
1150+
1151+
(previous, current, next, previous_url, next_url) = self.get_pages(previous_url)
1152+
1153+
assert previous is None
1154+
assert current == [None]
1155+
assert next == [None]
1156+
assert previous_url is None
1157+
1158+
10871159
def test_get_displayed_page_numbers():
10881160
"""
10891161
Test our contextual page display function.

0 commit comments

Comments
 (0)