Support for multiple filter classes

This commit is contained in:
Tom Christie 2013-05-07 13:00:44 +01:00
parent d71a5533f9
commit 3c2bb06660
7 changed files with 50 additions and 24 deletions

View File

@ -79,23 +79,22 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/
As well as being able to override the default queryset, REST framework also includes support for generic filtering backends that allow you to easily construct complex filters that can be specified by the client using query parameters.
REST framework supports pluggable backends to implement filtering, and provides an implementation which uses the [django-filter] package.
## DjangoFilterBackend
To use REST framework's filtering backend, first install `django-filter`.
To use REST framework's `DjangoFilterBackend`, first install `django-filter`.
pip install django-filter
You must also set the filter backend to `DjangoFilterBackend` in your settings:
REST_FRAMEWORK = {
'FILTER_BACKEND': 'rest_framework.filters.DjangoFilterBackend'
'DEFAULT_FILTER_BACKENDS': ['rest_framework.filters.DjangoFilterBackend']
}
## Specifying filter fields
#### Specifying filter fields
If all you need is simple equality-based filtering, you can set a `filter_fields` attribute on the view, or viewset,
listing the set of fields you wish to filter against.
If all you need is simple equality-based filtering, you can set a `filter_fields` attribute on the view, or viewset, listing the set of fields you wish to filter against.
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
@ -106,7 +105,7 @@ This will automatically create a `FilterSet` class for the given fields, and wil
http://example.com/api/products?category=clothing&in_stock=True
## Specifying a FilterSet
#### Specifying a FilterSet
For more advanced filtering requirements you can specify a `FilterSet` class that should be used by the view. For example:
@ -132,13 +131,13 @@ For more details on using filter sets see the [django-filter documentation][djan
**Hints & Tips**
* By default filtering is not enabled. If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'FILTER_BACKEND'` setting.
* By default filtering is not enabled. If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'DEFAULT_FILTER_BACKENDS'` setting.
* When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`. (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].)
* `django-filter` supports filtering across relationships, using Django's double-underscore syntax.
---
### Filtering and object lookups
## Filtering and object lookups
Note that if a filter backend is configured for a view, then as well as being used to filter list views, it will also be used to filter the querysets used for returning a single object.
@ -170,12 +169,12 @@ You can also provide your own generic filtering backend, or write an installable
To do so override `BaseFilterBackend`, and override the `.filter_queryset(self, request, queryset, view)` method. The method should return a new, filtered queryset.
To install the filter backend, set the `'FILTER_BACKEND'` key in your `'REST_FRAMEWORK'` setting, using the dotted import path of the filter backend class.
To install the filter backend, set the `'DEFAULT_FILTER_BACKENDS'` key in your `'REST_FRAMEWORK'` setting, using the dotted import path of the filter backend class.
For example:
REST_FRAMEWORK = {
'FILTER_BACKEND': 'custom_filters.CustomFilterBackend'
'DEFAULT_FILTER_BACKENDS': ['custom_filters.CustomFilterBackend']
}
[cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters

View File

@ -77,7 +77,7 @@ The following attibutes are used to control pagination when used with list views
**Filtering**:
* `filter_backend` - The filter backend class that should be used for filtering the queryset. Defaults to the same value as the `FILTER_BACKEND` setting.
* `filter_backends` - A list of filter backend classes that should be used for filtering the queryset. Defaults to the same value as the `DEFAULT_FILTER_BACKENDS` setting.
### Methods

View File

@ -112,9 +112,10 @@ A class the determines the default serialization style for paginated responses.
Default: `rest_framework.pagination.PaginationSerializer`
#### FILTER_BACKEND
#### DEFAULT_FILTER_BACKENDS
The filter backend class that should be used for generic filtering. If set to `None` then generic filtering is disabled.
A list of filter backend classes that should be used for generic filtering.
If set to `None` then generic filtering is disabled.
#### PAGINATE_BY

View File

@ -86,6 +86,14 @@ Similarly, you can now easily include the primary key in hyperlinked relationshi
model = Blog
fields = ('url', 'id', 'title', 'created', 'comments')
## More flexible filtering
The `FILTER_BACKEND` setting has moved to pending deprecation, in favor of a `DEFAULT_FILTER_BACKENDS` setting that takes a *list* of filter backend classes, instead of a single filter backend class.
The generic view `filter_backend` attribute has also been moved to pending deprecation in favor of a `filter_backends` setting.
Being able to specify multiple filters will allow for more flexible, powerful behavior. New filter classes to handle searching and ordering of results are planned to be released shortly.
---
# API Changes

View File

@ -46,6 +46,7 @@ You can determine your currently installed version using `pip freeze`:
* ModelSerializers support reverse relations in 'fields' option.
* HyperLinkedModelSerializers support 'id' field in 'fields' option.
* Cleaner generic views.
* Support for multiple filter classes.
* DecimalField support.
* Bugfix: Fix issue with depth>1 on ModelSerializer.

View File

@ -39,8 +39,8 @@ class GenericAPIView(views.APIView):
pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS
page_kwarg = 'page'
# The filter backend class to use for queryset filtering
filter_backend = api_settings.FILTER_BACKEND
# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# The following attributes may be subject to change,
# and should be considered private API.
@ -54,6 +54,7 @@ class GenericAPIView(views.APIView):
slug_url_kwarg = 'slug'
slug_field = 'slug'
allow_empty = True
filter_backend = api_settings.FILTER_BACKEND
def get_serializer_context(self):
"""
@ -150,10 +151,20 @@ class GenericAPIView(views.APIView):
method if you want to apply the configured filtering backend to the
default queryset.
"""
if not self.filter_backend:
filter_backends = self.filter_backends or []
if not filter_backends and self.filter_backend:
warnings.warn(
'The `filter_backend` attribute and `FILTER_BACKEND` setting '
'are due to be deprecated in favor of a `filter_backends` '
'attribute and `DEFAULT_FILTER_BACKENDS` setting, that take '
'a *list* of filter backend classes.',
PendingDeprecationWarning, stacklevel=2
)
filter_backends = [self.filter_backend]
for backend in filter_backends:
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
backend = self.filter_backend()
return backend.filter_queryset(self.request, queryset, self)
########################
### The following methods provide default implementations

View File

@ -29,6 +29,7 @@ from rest_framework.compat import six
USER_SETTINGS = getattr(settings, 'REST_FRAMEWORK', None)
DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
@ -50,11 +51,15 @@ DEFAULTS = {
'DEFAULT_CONTENT_NEGOTIATION_CLASS':
'rest_framework.negotiation.DefaultContentNegotiation',
# Genric view behavior
'DEFAULT_MODEL_SERIALIZER_CLASS':
'rest_framework.serializers.ModelSerializer',
'DEFAULT_PAGINATION_SERIALIZER_CLASS':
'rest_framework.pagination.PaginationSerializer',
'DEFAULT_FILTER_BACKENDS': (),
# Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
'anon': None,
@ -64,9 +69,6 @@ DEFAULTS = {
'PAGINATE_BY': None,
'PAGINATE_BY_PARAM': None,
# Filtering
'FILTER_BACKEND': None,
# Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,
@ -95,6 +97,9 @@ DEFAULTS = {
ISO_8601,
),
'TIME_FORMAT': ISO_8601,
# Pending deprecation
'FILTER_BACKEND': None,
}
@ -108,6 +113,7 @@ IMPORT_STRINGS = (
'DEFAULT_CONTENT_NEGOTIATION_CLASS',
'DEFAULT_MODEL_SERIALIZER_CLASS',
'DEFAULT_PAGINATION_SERIALIZER_CLASS',
'DEFAULT_FILTER_BACKENDS',
'FILTER_BACKEND',
'UNAUTHENTICATED_USER',
'UNAUTHENTICATED_TOKEN',