Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I stop django REST framework to show all records if query parameter is wrong

I am using Django REST Framework and i am using filters to filter the query set.

http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters

like this

http://example.com/api/products/4675/?category=clothing&max_price=10.00

But i have seen that if there is error in filters or the query parameters does not exist then it displays all results which is very bad.

I rather want no results if there is issue with query parameters because sometimes i don't know if that is working or not

EDIT

This is my code

class userFilter(django_filters.FilterSet):
    strict = True

    class Meta:
        model = User
        fields = ('is_active', 'is_archived', 'age')

REST

class UserListCreateView(generics.ListCreateAPIView):
    queryset = User.objects.filter(is_archived=False)
    ordering_fields = ('is_active')
    filter_class = userFilter

This is REST settings

REST_FRAMEWORK = {
    'DEFAULT_MODEL_SERIALIZER_CLASS':
        'rest_framework.serializers.HyperlinkedModelSerializer',
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_FILTER_BACKENDS': (
        'rest_framework.filters.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ),
    # 'PAGINATE_BY': 1,                 
    'PAGINATE_BY_PARAM': 'page_size',  
    'MAX_PAGINATE_BY': 100,             
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}
like image 400
user3214546 Avatar asked Nov 28 '14 05:11

user3214546


4 Answers

If you are using the DjangoFilterBackend, take a look at the strict Non-Meta option.

The strict option controls whether results are returned when an invalid value is specified by the user for any filter field. By default, strict is set to True meaning that an empty queryset is returned if any field contains an invalid value. You can loosen this behavior by setting strict to False which will effectively ignore a filter field if its value is invalid.

The filter:

from django_filters.filterset import FilterSet

class UserFilter(FilterSet):
    strict = True

    class Meta:
        model = User
        fields = ['username']

The settings: (assumes you have installed django-filter)

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',)
}

Now, if someone does:

http://api.example.com/users/?username=myuser&badfilter=1

...it will return an empty list, as badfilter does not exist.

As the FilterSet automatically defaults to strict=True, I have a feeling that you are not taking advantage of the DjangoFilterBackend.

like image 152
Michael B Avatar answered Oct 21 '22 19:10

Michael B


The marked answer didn't work for me. I solved it by overriding the "get" method:

class UserListCreateView(generics.ListCreateAPIView):
    queryset = User.objects.filter(is_archived=False)
    ordering_fields = ('is_active')
    filter_class = userFilter

    @staticmethod
    def is_valid_query_params(query_params):
        # do validations here
        ...

    def get(self, request, *args, **kwargs):
        if not self.is_valid_query_params(request.query_params):
            return Response([])  # send empty response 
        return super(UserListCreateView, self).get(request, *args, **kwargs)
like image 43
Kapil Ratnani Avatar answered Oct 21 '22 19:10

Kapil Ratnani


Your specific issue arises from the fact that the parameters you invoke in your GET query are not defined in your UserFilter. Thus only the following parameters will be taken into account from DRF:

fields = ('is_active', 'is_archived', 'age')

Moreover strict only controls the value of a query parameter, not if the parameter itself exists. For example

GET mydomain.com/resource_path?whatever=blabla

returns the whole queryset which is something wrong in my opinion, at least not REST-compliant.

I ended up writing a small method to manually check if the query parameters passed in the request actually exist.

like image 25
stelios Avatar answered Oct 21 '22 21:10

stelios


I did this by overloading the function get_queryset() in class which inherit generics.ListAPIView. You can check params with self.request.query_params , and write custom handler for any cases.

like image 1
Alex Avatar answered Oct 21 '22 20:10

Alex