Andy McKay

May 12, 2015

Stricter API filtering


Django Rest Framework and Tastypie both provide REST queries and allow looking up lists of objects based on query parameters. In the former case it allows multiple back-ends, but defaults to using Django filter.

That's a fine approach with one problem. Consider this:

http://example.com/api/products?category=clothing

You've got a reasonably good idea of what that will return. How about this one?

http://example.com/api/products?categry=clothing

What does that return? Everything. Because category got typo'd. Meaning that it was excluded from the filter and you didn't filter on anything at all. Ouch.

Out of the box Django Rest Framework only allows GETs on lists. Even a GET that goes awry like that is bad. But you can use the bulk library to do PATCH, PUT and DELETE.

DELETE http://example.com/api/products?categry=clothing

That's really going to ruin your day. Kumar rightly complained about this so I threw in a simple filter to stop that.

from rest_framework.exceptions import ParseError
from rest_framework.filters import DjangoFilterBackend

class StrictQueryFilter(DjangoFilterBackend):

    def filter_queryset(self, request, queryset, view):
        requested = set(request.QUERY_PARAMS.keys())
        allowed = set(getattr(view, 'filter_fields', []))
        difference = requested.difference(allowed)
        if difference:
            raise ParseError(
                detail='Incorrect query parameters: ' + ','.join(difference))

        return (super(self.__class__, self)
                .filter_queryset(request, queryset, view))

Once it's done alter: DEFAULT_FILTER_BACKENDS to point to that and make sure your filter_fields are correct.

Now a typo'd query string will return you a 422 error.