diff options
| -rwxr-xr-x | docs/api-guide/generic-views.md | 3 | ||||
| -rw-r--r-- | docs/topics/2.3-announcement.md | 14 | ||||
| -rw-r--r-- | rest_framework/filters.py | 29 | ||||
| -rw-r--r-- | rest_framework/generics.py | 12 |
4 files changed, 52 insertions, 6 deletions
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index d430710d..59912568 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -75,10 +75,9 @@ The following attibutes are used to control pagination when used with list views * `pagination_serializer_class` - The pagination serializer class to use when determining the style of paginated responses. Defaults to the same value as the `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting. * `page_kwarg` - The name of a URL kwarg or URL query parameter which can be used by the client to control which page is requested. Defaults to `'page'`. -**Other**: +**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. -* `allow_empty` - Determines if an empty list should successfully display zero results, or return a 404 response. Defaults to `True`, meaning empty lists will return sucessful `200 OK` responses, with zero results. ### Methods diff --git a/docs/topics/2.3-announcement.md b/docs/topics/2.3-announcement.md index c465b774..62fa5b9c 100644 --- a/docs/topics/2.3-announcement.md +++ b/docs/topics/2.3-announcement.md @@ -152,9 +152,21 @@ And would have the following entry in the urlconf: url(r'^users/(?P<username>\w+)/$', UserDetail.as_view()), - Usage of the old-style attributes continues to be supported, but will raise a `PendingDeprecationWarning`. +The `allow_empty` attribute is also deprecated. To use `allow_empty=False` style behavior you should explicitly override `get_queryset` and raise an `Http404` on empty querysets. + +For example: + + class DisallowEmptyQuerysetMixin(object): + def get_queryset(self): + queryset = super(DisallowEmptyQuerysetMixin, self).get_queryset() + if not queryset.exists(): + raise Http404 + return queryset + +In our opinion removing lesser-used attributes like `allow_empty` helps us move towards simpler generic view implementations, making them more obvious to use and override, and re-inforcing the preferred style of developers writing their own base classes and mixins for custom behavior rather than relying on the configurability of the generic views. + ## Simpler URL lookups The `HyperlinkedRelatedField` class now takes a single optional `lookup_field` argument, that replaces the `pk_url_kwarg`, `slug_url_kwarg`, and `slug_field` arguments. diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 5e1cdbac..571704dc 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -3,7 +3,10 @@ Provides generic filtering backends that can be used to filter the results returned by list views. """ from __future__ import unicode_literals + +from django.db import models from rest_framework.compat import django_filters +import operator FilterSet = django_filters and django_filters.FilterSet or None @@ -62,3 +65,29 @@ class DjangoFilterBackend(BaseFilterBackend): return filter_class(request.QUERY_PARAMS, queryset=queryset).qs return queryset + + +class SearchFilter(BaseFilterBackend): + def construct_search(self, field_name): + if field_name.startswith('^'): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith('='): + return "%s__iexact" % field_name[1:] + elif field_name.startswith('@'): + return "%s__search" % field_name[1:] + else: + return "%s__icontains" % field_name + + def filter_queryset(self, request, queryset, view): + search_fields = getattr(view, 'search_fields', None) + + if not search_fields: + return None + + orm_lookups = [self.construct_search(str(search_field)) + for search_field in self.search_fields] + for bit in self.query.split(): + or_queries = [models.Q(**{orm_lookup: bit}) + for orm_lookup in orm_lookups] + queryset = queryset.filter(reduce(operator.or_, or_queries)) + return queryset diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 62129dcc..2bb23a89 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -42,9 +42,6 @@ class GenericAPIView(views.APIView): # The filter backend class to use for queryset filtering filter_backend = api_settings.FILTER_BACKEND - # Determines if the view will return 200 or 404 responses for empty lists. - allow_empty = True - # The following attributes may be subject to change, # and should be considered private API. model_serializer_class = api_settings.DEFAULT_MODEL_SERIALIZER_CLASS @@ -56,6 +53,7 @@ class GenericAPIView(views.APIView): pk_url_kwarg = 'pk' slug_url_kwarg = 'slug' slug_field = 'slug' + allow_empty = True def get_serializer_context(self): """ @@ -111,6 +109,14 @@ class GenericAPIView(views.APIView): if not page_size: return None + if not self.allow_empty: + warnings.warn( + 'The `allow_empty` parameter is due to be deprecated. ' + 'To use `allow_empty=False` style behavior, You should override ' + '`get_queryset()` and explicitly raise a 404 on empty querysets.', + PendingDeprecationWarning, stacklevel=2 + ) + paginator = self.paginator_class(queryset, page_size, allow_empty_first_page=self.allow_empty) page_kwarg = self.kwargs.get(self.page_kwarg) |
