【问题标题】:Django REST Framework - FilteringDjango REST 框架 - 过滤
【发布时间】:2012-12-24 20:45:32
【问题描述】:

我想用多个查询过滤多个字段,如下所示:

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

是否有任何内置方式,我查看了 django-filters 但它似乎有限,我想我必须在我的 api 视图中手动执行此操作,但它变得混乱,过滤过滤器上的过滤器

【问题讨论】:

    标签: django django-filter django-rest-framework


    【解决方案1】:

    DRF 甚至 django-filter 插件都不支持开箱即用的复杂过滤器。对于简单的情况,您可以定义自己的 get_queryset 方法

    这直接来自文档

    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
    

    但是,如果您支持多个过滤器,甚至其中一些过滤器很复杂,这很快就会变得混乱。

    解决方案是定义一个自定义的 filterBackend 类和一个 ViewSet Mixin。这个 mixins 告诉视图集如何理解一个典型的过滤器后端,并且这个后端可以理解所有明确定义的非常复杂的过滤器,包括应该应用这些过滤器的规则。

    一个示例过滤器后端是这样的(我在不同的查询参数上按复杂度递增的顺序定义了三个不同的过滤器:

    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'
    

    ORM 查找的直接过滤器。

        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')
    

    更复杂的过滤器:

    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)
    

    要全面了解其工作原理,您可以阅读我的详细博文:http://iank.it/pluggable-filters-for-django-rest-framework/

    所有代码都在 Gist 中:https://gist.github.com/ankitml/fc8f4cf30ff40e19eae6

    【讨论】:

      【解决方案2】:

      在过滤器上过滤并不混乱,它被称为chained filters。

      链式过滤器是必要的,因为有时会有property_type 有时不会:

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

      如果您认为会有多个查询,那么它仍然会在一个查询中执行,因为查询集是惰性的。

      或者,您可以构建一个 dict 并只传递一次:

      d = {'property_type:': property_type, 'subburb': subburb}
      qs = MyModel.objects.filter(**d)
      

      【讨论】:

      • 但是像 property_type = house,townhouse 这样的多重部分呢
      • 我想将它们全部包含在查询中,因此每个 subburb 或过滤器是什么。
      • 好的,我有,像这样 Foo.objects.filter(name__in=["This", "That", "Those"])
      • @Harry 是的,将__in 用于多个值
      猜你喜欢
      • 2017-05-27
      • 2014-02-17
      • 2017-05-03
      • 1970-01-01
      • 2014-09-11
      • 2018-11-05
      • 2017-10-26
      • 2018-05-17
      • 2017-10-06
      相关资源
      最近更新 更多