mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-18 12:12:19 +03:00
Fix ordering duplication on non-unique field
Co-authored-by: Roman Gorbil <roman.gorbil@saritasa.com>
This commit is contained in:
parent
4bbfa8d455
commit
e16f3852b4
|
@ -233,16 +233,52 @@ class OrderingFilter(BaseFilterBackend):
|
||||||
The `ordering` query parameter can be overridden by setting
|
The `ordering` query parameter can be overridden by setting
|
||||||
the `ordering_param` value on the OrderingFilter or by
|
the `ordering_param` value on the OrderingFilter or by
|
||||||
specifying an `ORDERING_PARAM` value in the API settings.
|
specifying an `ORDERING_PARAM` value in the API settings.
|
||||||
|
|
||||||
|
Always add "pk" as last parameter for ordering.
|
||||||
|
|
||||||
|
For example, we have following QS
|
||||||
|
| pk | name |
|
||||||
|
|----|------|
|
||||||
|
| 1 | Bob |
|
||||||
|
| 2 | Dan |
|
||||||
|
| 3 | Bob |
|
||||||
|
| 4 | Joe |
|
||||||
|
|
||||||
|
And we want to order it by name and take 2nd element.
|
||||||
|
We can get following results:
|
||||||
|
`qs.order_by('name')`
|
||||||
|
| pk | name |
|
||||||
|
|----|------|
|
||||||
|
| 1 | Bob |
|
||||||
|
| 3 | Bob | <- pk 3 is 2nd element
|
||||||
|
| 2 | Dan |
|
||||||
|
| 4 | Joe |
|
||||||
|
|
||||||
|
Or we can get:
|
||||||
|
| pk | name |
|
||||||
|
|----|------|
|
||||||
|
| 3 | Bob |
|
||||||
|
| 1 | Bob | <- pk 1 is 2nd element
|
||||||
|
| 2 | Dan |
|
||||||
|
| 4 | Joe |
|
||||||
|
|
||||||
|
As you see, QS is correctly ordered, but order is not consistent, and
|
||||||
|
pagination is also inconsistent since we are using fields which doesn't
|
||||||
|
have unique restriction. So we always add "pk" as last ordering param.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
params = request.query_params.get(self.ordering_param)
|
params = request.query_params.get(self.ordering_param)
|
||||||
if params:
|
if params:
|
||||||
fields = [param.strip() for param in params.split(',')]
|
fields = [param.strip() for param in params.split(',')]
|
||||||
ordering = self.remove_invalid_fields(queryset, fields, view, request)
|
ordering = self.remove_invalid_fields(queryset, fields, view, request)
|
||||||
if ordering:
|
if ordering:
|
||||||
return ordering
|
return ordering + ["pk"]
|
||||||
|
|
||||||
# No ordering was included, or all the ordering fields were invalid
|
# No ordering was included, or all the ordering fields were invalid
|
||||||
return self.get_default_ordering(view)
|
default_ordering = self.get_default_ordering(view)
|
||||||
|
if not default_ordering:
|
||||||
|
return default_ordering
|
||||||
|
return default_ordering + ("pk",)
|
||||||
|
|
||||||
def get_default_ordering(self, view):
|
def get_default_ordering(self, view):
|
||||||
ordering = getattr(view, 'ordering', None)
|
ordering = getattr(view, 'ordering', None)
|
||||||
|
|
|
@ -624,15 +624,15 @@ class CursorPaginationTestsMixin:
|
||||||
|
|
||||||
request = Request(factory.get('/', {'ordering': 'username'}))
|
request = Request(factory.get('/', {'ordering': 'username'}))
|
||||||
ordering = self.pagination.get_ordering(request, [], MockView())
|
ordering = self.pagination.get_ordering(request, [], MockView())
|
||||||
assert ordering == ('username',)
|
assert ordering == ('username', 'pk')
|
||||||
|
|
||||||
request = Request(factory.get('/', {'ordering': '-username'}))
|
request = Request(factory.get('/', {'ordering': '-username'}))
|
||||||
ordering = self.pagination.get_ordering(request, [], MockView())
|
ordering = self.pagination.get_ordering(request, [], MockView())
|
||||||
assert ordering == ('-username',)
|
assert ordering == ('-username', 'pk')
|
||||||
|
|
||||||
request = Request(factory.get('/', {'ordering': 'invalid'}))
|
request = Request(factory.get('/', {'ordering': 'invalid'}))
|
||||||
ordering = self.pagination.get_ordering(request, [], MockView())
|
ordering = self.pagination.get_ordering(request, [], MockView())
|
||||||
assert ordering == ('created',)
|
assert ordering == ('created', 'pk')
|
||||||
|
|
||||||
def test_use_with_ordering_filter_without_ordering_default_value(self):
|
def test_use_with_ordering_filter_without_ordering_default_value(self):
|
||||||
class MockView:
|
class MockView:
|
||||||
|
@ -646,7 +646,7 @@ class CursorPaginationTestsMixin:
|
||||||
|
|
||||||
request = Request(factory.get('/', {'ordering': 'username'}))
|
request = Request(factory.get('/', {'ordering': 'username'}))
|
||||||
ordering = self.pagination.get_ordering(request, [], MockView())
|
ordering = self.pagination.get_ordering(request, [], MockView())
|
||||||
assert ordering == ('username',)
|
assert ordering == ('username', "pk")
|
||||||
|
|
||||||
request = Request(factory.get('/', {'ordering': 'invalid'}))
|
request = Request(factory.get('/', {'ordering': 'invalid'}))
|
||||||
ordering = self.pagination.get_ordering(request, [], MockView())
|
ordering = self.pagination.get_ordering(request, [], MockView())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user