Skip to content

Commit f1a11d4

Browse files
authored
fix: fallback on CursorPagination ordering if unset on the view (#8954)
* this commit fixes the usage of a CursorPagination combined with a view implementing an ordering filter, without a default ordering value. * former behavior was to fetch the ordering value from the filter, and raises an error if the value was None, preventing the fallback on the ordering set on the CursorPagination class itself. * we reversed the logic by getting first the value set on the class, and override it by the ordering filter if the parameter is present
1 parent 54307a4 commit f1a11d4

File tree

2 files changed

+35
-20
lines changed

2 files changed

+35
-20
lines changed

rest_framework/pagination.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,10 @@ def get_ordering(self, request, queryset, view):
801801
"""
802802
Return a tuple of strings, that may be used in an `order_by` method.
803803
"""
804+
# The default case is to check for an `ordering` attribute
805+
# on this pagination instance.
806+
ordering = self.ordering
807+
804808
ordering_filters = [
805809
filter_cls for filter_cls in getattr(view, 'filter_backends', [])
806810
if hasattr(filter_cls, 'get_ordering')
@@ -811,26 +815,19 @@ def get_ordering(self, request, queryset, view):
811815
# then we defer to that filter to determine the ordering.
812816
filter_cls = ordering_filters[0]
813817
filter_instance = filter_cls()
814-
ordering = filter_instance.get_ordering(request, queryset, view)
815-
assert ordering is not None, (
816-
'Using cursor pagination, but filter class {filter_cls} '
817-
'returned a `None` ordering.'.format(
818-
filter_cls=filter_cls.__name__
819-
)
820-
)
821-
else:
822-
# The default case is to check for an `ordering` attribute
823-
# on this pagination instance.
824-
ordering = self.ordering
825-
assert ordering is not None, (
826-
'Using cursor pagination, but no ordering attribute was declared '
827-
'on the pagination class.'
828-
)
829-
assert '__' not in ordering, (
830-
'Cursor pagination does not support double underscore lookups '
831-
'for orderings. Orderings should be an unchanging, unique or '
832-
'nearly-unique field on the model, such as "-created" or "pk".'
833-
)
818+
ordering_from_filter = filter_instance.get_ordering(request, queryset, view)
819+
if ordering_from_filter:
820+
ordering = ordering_from_filter
821+
822+
assert ordering is not None, (
823+
'Using cursor pagination, but no ordering attribute was declared '
824+
'on the pagination class.'
825+
)
826+
assert '__' not in ordering, (
827+
'Cursor pagination does not support double underscore lookups '
828+
'for orderings. Orderings should be an unchanging, unique or '
829+
'nearly-unique field on the model, such as "-created" or "pk".'
830+
)
834831

835832
assert isinstance(ordering, (str, list, tuple)), (
836833
'Invalid ordering. Expected string or tuple, but got {type}'.format(

tests/test_pagination.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,24 @@ class MockView:
632632
ordering = self.pagination.get_ordering(request, [], MockView())
633633
assert ordering == ('created',)
634634

635+
def test_use_with_ordering_filter_without_ordering_default_value(self):
636+
class MockView:
637+
filter_backends = (filters.OrderingFilter,)
638+
ordering_fields = ['username', 'created']
639+
640+
request = Request(factory.get('/'))
641+
ordering = self.pagination.get_ordering(request, [], MockView())
642+
# it gets the value of `ordering` provided by CursorPagination
643+
assert ordering == ('created',)
644+
645+
request = Request(factory.get('/', {'ordering': 'username'}))
646+
ordering = self.pagination.get_ordering(request, [], MockView())
647+
assert ordering == ('username',)
648+
649+
request = Request(factory.get('/', {'ordering': 'invalid'}))
650+
ordering = self.pagination.get_ordering(request, [], MockView())
651+
assert ordering == ('created',)
652+
635653
def test_cursor_pagination(self):
636654
(previous, current, next, previous_url, next_url) = self.get_pages('/')
637655

0 commit comments

Comments
 (0)