@@ -951,17 +951,24 @@ class MockQuerySet:
951
951
def __init__ (self , items ):
952
952
self .items = items
953
953
954
- def filter (self , created__gt = None , created__lt = None ):
954
+ def filter (self , q ):
955
+ q_args = dict (q .deconstruct ()[1 ])
956
+ if not q_args :
957
+ # django 3.0.x artifact
958
+ q_args = dict (q .deconstruct ()[2 ])
959
+ created__gt = q_args .get ('created__gt' )
960
+ created__lt = q_args .get ('created__lt' )
961
+
955
962
if created__gt is not None :
956
963
return MockQuerySet ([
957
964
item for item in self .items
958
- if item .created > int (created__gt )
965
+ if item .created is None or item . created > int (created__gt )
959
966
])
960
967
961
968
assert created__lt is not None
962
969
return MockQuerySet ([
963
970
item for item in self .items
964
- if item .created < int (created__lt )
971
+ if item .created is None or item . created < int (created__lt )
965
972
])
966
973
967
974
def order_by (self , * ordering ):
@@ -1080,6 +1087,127 @@ def get_pages(self, url):
1080
1087
return (previous , current , next , previous_url , next_url )
1081
1088
1082
1089
1090
+ class NullableCursorPaginationModel (models .Model ):
1091
+ created = models .IntegerField (null = True )
1092
+
1093
+
1094
+ class TestCursorPaginationWithNulls (TestCase ):
1095
+ """
1096
+ Unit tests for `pagination.CursorPagination` with ordering on a nullable field.
1097
+ """
1098
+
1099
+ def setUp (self ):
1100
+ class ExamplePagination (pagination .CursorPagination ):
1101
+ page_size = 1
1102
+ ordering = 'created'
1103
+
1104
+ self .pagination = ExamplePagination ()
1105
+ data = [
1106
+ None , None , 3 , 4
1107
+ ]
1108
+ for idx in data :
1109
+ NullableCursorPaginationModel .objects .create (created = idx )
1110
+
1111
+ self .queryset = NullableCursorPaginationModel .objects .all ()
1112
+
1113
+ get_pages = TestCursorPagination .get_pages
1114
+
1115
+ def test_ascending (self ):
1116
+ """Test paginating one row at a time, current should go 1, 2, 3, 4, 3, 2, 1."""
1117
+ (previous , current , next , previous_url , next_url ) = self .get_pages ('/' )
1118
+
1119
+ assert previous is None
1120
+ assert current == [None ]
1121
+ assert next == [None ]
1122
+
1123
+ (previous , current , next , previous_url , next_url ) = self .get_pages (next_url )
1124
+
1125
+ assert previous == [None ]
1126
+ assert current == [None ]
1127
+ assert next == [3 ]
1128
+
1129
+ (previous , current , next , previous_url , next_url ) = self .get_pages (next_url )
1130
+
1131
+ assert previous == [3 ] # [None] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L789
1132
+ assert current == [3 ]
1133
+ assert next == [4 ]
1134
+
1135
+ (previous , current , next , previous_url , next_url ) = self .get_pages (next_url )
1136
+
1137
+ assert previous == [3 ]
1138
+ assert current == [4 ]
1139
+ assert next is None
1140
+ assert next_url is None
1141
+
1142
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1143
+
1144
+ assert previous == [None ]
1145
+ assert current == [3 ]
1146
+ assert next == [4 ]
1147
+
1148
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1149
+
1150
+ assert previous == [None ]
1151
+ assert current == [None ]
1152
+ assert next == [None ] # [3] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L731
1153
+
1154
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1155
+
1156
+ assert previous is None
1157
+ assert current == [None ]
1158
+ assert next == [None ]
1159
+
1160
+ def test_descending (self ):
1161
+ """Test paginating one row at a time, current should go 4, 3, 2, 1, 2, 3, 4."""
1162
+ self .pagination .ordering = ('-created' ,)
1163
+ (previous , current , next , previous_url , next_url ) = self .get_pages ('/' )
1164
+
1165
+ assert previous is None
1166
+ assert current == [4 ]
1167
+ assert next == [3 ]
1168
+
1169
+ (previous , current , next , previous_url , next_url ) = self .get_pages (next_url )
1170
+
1171
+ assert previous == [None ] # [4] paging artifact
1172
+ assert current == [3 ]
1173
+ assert next == [None ]
1174
+
1175
+ (previous , current , next , previous_url , next_url ) = self .get_pages (next_url )
1176
+
1177
+ assert previous == [None ] # [3] paging artifact
1178
+ assert current == [None ]
1179
+ assert next == [None ]
1180
+
1181
+ (previous , current , next , previous_url , next_url ) = self .get_pages (next_url )
1182
+
1183
+ assert previous == [None ]
1184
+ assert current == [None ]
1185
+ assert next is None
1186
+ assert next_url is None
1187
+
1188
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1189
+
1190
+ assert previous == [3 ]
1191
+ assert current == [None ]
1192
+ assert next == [None ]
1193
+
1194
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1195
+
1196
+ assert previous == [None ]
1197
+ assert current == [3 ]
1198
+ assert next == [3 ] # [4] paging artifact documented at https://github.com/ddelange/django-rest-framework/blob/3.14.0/rest_framework/pagination.py#L731
1199
+
1200
+ # skip back artifact
1201
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1202
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1203
+
1204
+ (previous , current , next , previous_url , next_url ) = self .get_pages (previous_url )
1205
+
1206
+ assert previous is None
1207
+ assert current == [4 ]
1208
+ assert next == [3 ]
1209
+
1210
+
1083
1211
def test_get_displayed_page_numbers ():
1084
1212
"""
1085
1213
Test our contextual page display function.
0 commit comments