mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-03-03 10:45:51 +03:00
Added OrderingFilter
This commit is contained in:
parent
08bc976269
commit
6a037f63ed
|
@ -190,7 +190,7 @@ The `SearchFilterBackend` class will only be applied if the view has a `search_f
|
|||
filter_backends = (filters.SearchFilter,)
|
||||
search_fields = ('username', 'email')
|
||||
|
||||
This will allow the client to filter the itemss in the list by making queries such as:
|
||||
This will allow the client to filter the items in the list by making queries such as:
|
||||
|
||||
http://example.com/api/users?search=russell
|
||||
|
||||
|
@ -198,7 +198,7 @@ You can also perform a related lookup on a ForeignKey or ManyToManyField with th
|
|||
|
||||
search_fields = ('username', 'email', 'profile__profession')
|
||||
|
||||
By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma seperated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched.
|
||||
By default, searches will use case-insensitive partial matches. The search parameter may contain multiple search terms, which should be whitespace and/or comma separated. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched.
|
||||
|
||||
The search behavior may be restricted by prepending various characters to the `search_fields`.
|
||||
|
||||
|
@ -214,6 +214,34 @@ For more details, see the [Django documentation][search-django-admin].
|
|||
|
||||
---
|
||||
|
||||
## OrderingFilter
|
||||
|
||||
The `OrderingFilter` class supports simple query parameter controlled ordering of results. For example:
|
||||
|
||||
http://example.com/api/users?ordering=username
|
||||
|
||||
The client may also specify reverse orderings by prefixing the field name with '-', like so:
|
||||
|
||||
http://example.com/api/users?ordering=-username
|
||||
|
||||
Multiple orderings may also be specified:
|
||||
|
||||
http://example.com/api/users?ordering=account,username
|
||||
|
||||
If an `ordering` attribute is set on the view, this will be used as the default ordering.
|
||||
|
||||
Typicaly you'd instead control this by setting `order_by` on the initial queryset, but using the `ordering` parameter on the view allows you to specify the ordering in a way that it can then be passed automatically as context to a rendered template. This makes it possible to automatically render column headers differently if they are being used to order the results.
|
||||
|
||||
class UserListView(generics.ListAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer = UserSerializer
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('username',)
|
||||
|
||||
The `ordering` attribute may be either a string or a list/tuple of strings.
|
||||
|
||||
---
|
||||
|
||||
# Custom generic filtering
|
||||
|
||||
You can also provide your own generic filtering backend, or write an installable app for other developers to use.
|
||||
|
|
|
@ -4,7 +4,7 @@ returned by list views.
|
|||
"""
|
||||
from __future__ import unicode_literals
|
||||
from django.db import models
|
||||
from rest_framework.compat import django_filters
|
||||
from rest_framework.compat import django_filters, six
|
||||
from functools import reduce
|
||||
import operator
|
||||
|
||||
|
@ -109,3 +109,42 @@ class SearchFilter(BaseFilterBackend):
|
|||
queryset = queryset.filter(reduce(operator.or_, or_queries))
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
class OrderingFilter(BaseFilterBackend):
|
||||
ordering_param = 'order' # The URL query parameter used for the ordering.
|
||||
|
||||
def get_ordering(self, request):
|
||||
"""
|
||||
Search terms are set by a ?search=... query parameter,
|
||||
and may be comma and/or whitespace delimited.
|
||||
"""
|
||||
params = request.QUERY_PARAMS.get(self.ordering_param)
|
||||
if params:
|
||||
return [param.strip() for param in params.split(',')]
|
||||
|
||||
def get_default_ordering(self, view):
|
||||
ordering = getattr(view, 'ordering', None)
|
||||
if isinstance(ordering, six.string_types):
|
||||
return (ordering,)
|
||||
return ordering
|
||||
|
||||
def remove_invalid_fields(self, queryset, ordering):
|
||||
field_names = [field.name for field in queryset.model._meta.fields]
|
||||
return [term for term in ordering if term.lstrip('-') in field_names]
|
||||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
ordering = self.get_ordering(request)
|
||||
|
||||
if ordering:
|
||||
# Skip any incorrect parameters
|
||||
ordering = self.remove_invalid_fields(queryset, ordering)
|
||||
|
||||
if not ordering:
|
||||
# Use 'ordering' attribtue by default
|
||||
ordering = self.get_default_ordering(view)
|
||||
|
||||
if ordering:
|
||||
return queryset.order_by(*ordering)
|
||||
|
||||
return queryset
|
||||
|
|
|
@ -335,3 +335,119 @@ class SearchFilterTests(TestCase):
|
|||
{'id': 2, 'title': 'zz', 'text': 'bcd'}
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class OrdringFilterModel(models.Model):
|
||||
title = models.CharField(max_length=20)
|
||||
text = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class OrderingFilterTests(TestCase):
|
||||
def setUp(self):
|
||||
# Sequence of title/text is:
|
||||
#
|
||||
# zyx abc
|
||||
# yxw bcd
|
||||
# xwv cde
|
||||
for idx in range(3):
|
||||
title = (
|
||||
chr(ord('z') - idx) +
|
||||
chr(ord('y') - idx) +
|
||||
chr(ord('x') - idx)
|
||||
)
|
||||
text = (
|
||||
chr(idx + ord('a')) +
|
||||
chr(idx + ord('b')) +
|
||||
chr(idx + ord('c'))
|
||||
)
|
||||
OrdringFilterModel(title=title, text=text).save()
|
||||
|
||||
def test_ordering(self):
|
||||
class OrderingListView(generics.ListAPIView):
|
||||
model = OrdringFilterModel
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('title',)
|
||||
|
||||
view = OrderingListView.as_view()
|
||||
request = factory.get('?order=text')
|
||||
response = view(request)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
[
|
||||
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
||||
{'id': 2, 'title': 'yxw', 'text': 'bcd'},
|
||||
{'id': 3, 'title': 'xwv', 'text': 'cde'},
|
||||
]
|
||||
)
|
||||
|
||||
def test_reverse_ordering(self):
|
||||
class OrderingListView(generics.ListAPIView):
|
||||
model = OrdringFilterModel
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('title',)
|
||||
|
||||
view = OrderingListView.as_view()
|
||||
request = factory.get('?order=-text')
|
||||
response = view(request)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
[
|
||||
{'id': 3, 'title': 'xwv', 'text': 'cde'},
|
||||
{'id': 2, 'title': 'yxw', 'text': 'bcd'},
|
||||
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
||||
]
|
||||
)
|
||||
|
||||
def test_incorrectfield_ordering(self):
|
||||
class OrderingListView(generics.ListAPIView):
|
||||
model = OrdringFilterModel
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('title',)
|
||||
|
||||
view = OrderingListView.as_view()
|
||||
request = factory.get('?order=foobar')
|
||||
response = view(request)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
[
|
||||
{'id': 3, 'title': 'xwv', 'text': 'cde'},
|
||||
{'id': 2, 'title': 'yxw', 'text': 'bcd'},
|
||||
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
||||
]
|
||||
)
|
||||
|
||||
def test_default_ordering(self):
|
||||
class OrderingListView(generics.ListAPIView):
|
||||
model = OrdringFilterModel
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('title',)
|
||||
|
||||
view = OrderingListView.as_view()
|
||||
request = factory.get('')
|
||||
response = view(request)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
[
|
||||
{'id': 3, 'title': 'xwv', 'text': 'cde'},
|
||||
{'id': 2, 'title': 'yxw', 'text': 'bcd'},
|
||||
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
||||
]
|
||||
)
|
||||
|
||||
def test_default_ordering_using_string(self):
|
||||
class OrderingListView(generics.ListAPIView):
|
||||
model = OrdringFilterModel
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = 'title'
|
||||
|
||||
view = OrderingListView.as_view()
|
||||
request = factory.get('')
|
||||
response = view(request)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
[
|
||||
{'id': 3, 'title': 'xwv', 'text': 'cde'},
|
||||
{'id': 2, 'title': 'yxw', 'text': 'bcd'},
|
||||
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
||||
]
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user