Django REST Framework - Filtering

后端 未结 2 1677
伪装坚强ぢ
伪装坚强ぢ 2020-12-21 15:00

I want to filter multiple fields with multiple queries like this:

api/listings/?subburb=Subburb1, Subburb2&property_type=House,Apartment,Townhouse,Farm .         


        
相关标签:
2条回答
  • 2020-12-21 15:29

    filtering on filters on filters is not messy it is called chained filters.

    And chain filters are necessary because sometime there is going to be property_type some time not:

    if property_type:
        qs = qs.filter(property_type=property_type)
    

    If you are thinking there is going to be multiple queries then not, it will still executed in one query because queryset are lazy.

    Alternatively you can build a dict and pass it just one time:

    d = {'property_type:': property_type, 'subburb': subburb}
    qs = MyModel.objects.filter(**d)
    
    0 讨论(0)
  • 2020-12-21 15:29

    Complex filters are not out of the box supported by DRF or even by django-filter plugin. For simple cases you can define your own get_queryset method

    This is straight from the documentation

    def get_queryset(self):
        queryset = Purchase.objects.all()
        username = self.request.query_params.get('username', None)
        if username is not None:
            queryset = queryset.filter(purchaser__username=username)
        return queryset
    

    However this can quickly become messy if you are supported multiple filters and even some of them complex.

    The solution is to define a custom filterBackend class and a ViewSet Mixin. This mixins tells the viewset how to understand a typical filter backend and this backend can understand very complex filters all defined explicitly, including rules when those filters should be applied.

    A sample filter backend is like this (I have defined three different filters on different query parameters in the increasing order of complexity:

    class SomeFiltersBackend(FiltersBackendBase):
        """
        Filter backend class to compliment GenericFilterMixin from utils/mixin.
        """
        mapping = {'owner': 'filter_by_owner',
                   'catness': 'filter_by_catness',
                   'context': 'filter_by_context'}
        def rule(self):
            return resolve(self.request.path_info).url_name == 'pet-owners-list'
    

    Straight forward filter on ORM lookups.

        def filter_by_catness(self, value):
            """
            A simple filter to display owners of pets with high catness, canines excuse. 
            """
            catness = self.request.query_params.get('catness')
            return Q(owner__pet__catness__gt=catness)
    
        def filter_by_owner(self, value):
            if value == 'me':
                return Q(owner=self.request.user.profile)
            elif value.isdigit():
                try:
                    profile = PetOwnerProfile.objects.get(user__id=value)
                except PetOwnerProfile.DoesNotExist:
                    raise ValidationError('Owner does not exist')
                return Q(owner=profile)
            else:
                raise ValidationError('Wrong filter applied with owner')
    

    More complex filters :

    def filter_by_context(self, value):
        """
        value = {"context_type" : "context_id or context_ids separated by comma"}
        """
        import json
        try:
            context = json.loads(value)
        except json.JSONDecodeError as e:
            raise ValidationError(e)
    
        context_type, context_ids = context.items()
        context_ids = [int(i) for i in context_ids]
        if context_type == 'default':
            ids = context_ids
        else:
            ids = Context.get_ids_by_unsupported_contexts(context_type, context_ids)
        else:
            raise ValidationError('Wrong context type found')
        return Q(context_id__in=ids)
    

    To understand fully how this works, you can read up my detailed blogpost : http://iank.it/pluggable-filters-for-django-rest-framework/

    All the code is there in a Gist as well : https://gist.github.com/ankitml/fc8f4cf30ff40e19eae6

    0 讨论(0)
提交回复
热议问题