DRF - 是否可以将 URL 中的多个过滤器参数与某种 OR 逻辑符号组合
DRF - is it possible to combine multiple filter parameters in the URL with some kind of OR logical symbol
我使用 Django REST Framework 构建了一个 REST 端点。
class PersonFilter(django_filters.FilterSet):
id = django_filters.NumberFilter(name="id", lookup_type="gt")
first_name = django_filters.CharFilter(name="first_name", lookup_type="icontains")
last_name = django_filters.CharFilter(name="last_name", lookup_type="icontains")
class Meta:
model = Person
fields = ('id', 'first_name', 'last_name', 'last_mod')
class PersonModelViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Person.objects.none()
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
pagination_class = StandardResultsSetPagination
ordering_fields = ('id', 'first_name', 'last_name', 'last_mod')
ordering = ('last_mod', 'id')
filter_class = PersonFilter
现在如果我提出这样的请求:
/api/rest/v1/Person?first_name=foo&last_name=foo&page_size=10
这 return 仅包含名字和姓氏都包含 "foo" 的对象。我想 return 那些名字包含 "foo" 或姓氏包含 "foo".
的对象
我想知道URL参数中是否有可用的符号,表示过滤器之间的逻辑或关系。
一种解决方法是向端点发出两个单独的 AJAX 查询,但这需要额外的工作来统一结果。
遗憾的是,目前的 django_filter
实现无法实现。每个过滤器都会修改查询集 in-place 而不是返回 Q
对象,这可以根据您的喜好加入。您可以尝试覆盖 FilterSet.qs()
方法并在 self._qs.query.where
上使用一些黑魔法来使用 OR
重新组合子句。另见 editing the queryset filters.
上的问题
更新:只要 Django 很好地处理 SQL 注入尝试,您就可以使用类似的东西:
qs.filter(map(operators.or_, [Q(k=v) for k, v in request.GET.items()]))
,但在投入生产之前肯定需要一些验证。
我也想做类似的事情,最后使用 django-filter 创建了一个自定义过滤器,希望这对您有所帮助:
class NameFilter(django_filters.CharFilter):
def filter(self, qs, value):
if value:
return qs.filter(Q(**{first_name+'__'+self.lookup_expr: value}) |
Q(**{last_name+'__'+self.lookup_expr: value}))
return qs
class PersonFilter(django_filters.rest_framework.FilterSet):
name = NameFilter(lookup_expr='icontains')
/api/rest/v1/Person?name=foo&page_size=10
不是一个非常通用的解决方案,但它是一个如何创建您自己的过滤器的示例,它的通用性取决于您的代码实现。
您可以使用复杂的单词并使用不同的前缀(不仅'q')
query_reg = self.request.GET.get("r")
if query_reg:
queryset = queryset.filter(
Q(region=query_reg)
)
query_reg_air = self.request.GET.get("ra")
if query_reg_air:
queryset = queryset.filter(
Q(region=query_reg_air.split('_')[0]) &
Q(min_air_flow__lte=query_reg_air.split('_')[1]) &
Q(max_air_flow__gte=query_reg_air.split('_')[1]) |
Q(region=query_reg_air.split('_')[0]) &
Q(min_air_flow__lte=str(float(query_reg_air.split('_')[1]) * 1.2)) &
Q(max_air_flow__gte=str(float(query_reg_air.split('_')[1])))
# Q(query__range=(min_air_flow, max_air_flow))
).order_by('-KPC')
我使用 Django REST Framework 构建了一个 REST 端点。
class PersonFilter(django_filters.FilterSet):
id = django_filters.NumberFilter(name="id", lookup_type="gt")
first_name = django_filters.CharFilter(name="first_name", lookup_type="icontains")
last_name = django_filters.CharFilter(name="last_name", lookup_type="icontains")
class Meta:
model = Person
fields = ('id', 'first_name', 'last_name', 'last_mod')
class PersonModelViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Person.objects.none()
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
pagination_class = StandardResultsSetPagination
ordering_fields = ('id', 'first_name', 'last_name', 'last_mod')
ordering = ('last_mod', 'id')
filter_class = PersonFilter
现在如果我提出这样的请求:
/api/rest/v1/Person?first_name=foo&last_name=foo&page_size=10
这 return 仅包含名字和姓氏都包含 "foo" 的对象。我想 return 那些名字包含 "foo" 或姓氏包含 "foo".
的对象我想知道URL参数中是否有可用的符号,表示过滤器之间的逻辑或关系。
一种解决方法是向端点发出两个单独的 AJAX 查询,但这需要额外的工作来统一结果。
遗憾的是,目前的 django_filter
实现无法实现。每个过滤器都会修改查询集 in-place 而不是返回 Q
对象,这可以根据您的喜好加入。您可以尝试覆盖 FilterSet.qs()
方法并在 self._qs.query.where
上使用一些黑魔法来使用 OR
重新组合子句。另见 editing the queryset filters.
更新:只要 Django 很好地处理 SQL 注入尝试,您就可以使用类似的东西:
qs.filter(map(operators.or_, [Q(k=v) for k, v in request.GET.items()]))
,但在投入生产之前肯定需要一些验证。
我也想做类似的事情,最后使用 django-filter 创建了一个自定义过滤器,希望这对您有所帮助:
class NameFilter(django_filters.CharFilter):
def filter(self, qs, value):
if value:
return qs.filter(Q(**{first_name+'__'+self.lookup_expr: value}) |
Q(**{last_name+'__'+self.lookup_expr: value}))
return qs
class PersonFilter(django_filters.rest_framework.FilterSet):
name = NameFilter(lookup_expr='icontains')
/api/rest/v1/Person?name=foo&page_size=10
不是一个非常通用的解决方案,但它是一个如何创建您自己的过滤器的示例,它的通用性取决于您的代码实现。
您可以使用复杂的单词并使用不同的前缀(不仅'q')
query_reg = self.request.GET.get("r")
if query_reg:
queryset = queryset.filter(
Q(region=query_reg)
)
query_reg_air = self.request.GET.get("ra")
if query_reg_air:
queryset = queryset.filter(
Q(region=query_reg_air.split('_')[0]) &
Q(min_air_flow__lte=query_reg_air.split('_')[1]) &
Q(max_air_flow__gte=query_reg_air.split('_')[1]) |
Q(region=query_reg_air.split('_')[0]) &
Q(min_air_flow__lte=str(float(query_reg_air.split('_')[1]) * 1.2)) &
Q(max_air_flow__gte=str(float(query_reg_air.split('_')[1])))
# Q(query__range=(min_air_flow, max_air_flow))
).order_by('-KPC')