我正在使用drf和django-filters对我的数据执行一些过滤。对于过滤后端,我使用django_filters.rest_framework.DjangoFilterBackend
。
class ProductsViewSet(LoginRequiredMixin, ModelViewSet):
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,
rest_framework.filters.SearchFilter,
rest_framework.filters.OrderingFilter,)
filter_class = ProductFilter
search_fields = ("title", "description", "company", "country", "status",)
ordering_fields = ("title", "description", "company", "country", "status",)
我的filter_class:
class ProductFilter(filters.FilterSet):
price_from = filters.NumberFilter(field_name="price", lookup_expr="gte",
label="Min price",
min_value=0)
price_to = filters.NumberFilter(field_name="price", lookup_expr="lte",
label="Max price",
min_value=0)
# MultipleChoiceFilters
country = filters.MultipleChoiceFilter(field_name="country", choices=COUNTRY_CHOICES)
company = filters.MultipleChoiceFilter(field_name="company", choices=COMPANY_CHOICES)
status = filters.MultipleChoiceFilter(field_name="status", choices=STATUS_CHOICES)
class Meta:
model = Product
fields = ["price_from", "price_to", "country",
"company", "status"]
@property
def qs(self):
parent = super(ProductFilter, self).qs
return parent.order_by("-timestamp")
def __init__(self, *args, **kwargs):
super(ProductFilter, self).__init__(*args, **kwargs)
user = self.request.user
user_products = Product.objects.filter(user=user)
# Initial Data #############################################################
price_min = user_products.all().aggregate(Min("price"))["price__min"]
price_max = user_products.all().aggregate(Max("price"))["price__max"]
self.filters["price_from"].extra["initial"] = price_min
self.filters["price_to"].extra["initial"] = price_max
COUNTRY_CHOICES = tuple(Product.objects.filter(user=user).values_list("country", "country__name").distinct().order_by("country__name"))
self.filters["country"].extra['choices'] = COUNTRY_CHOICES
COMPANY_CHOICES = tuple(Product.objects.filter(user=user).values_list("company", "company").distinct().order_by("company"))
self.filters["company"].extra['choices'] = COMPANY_CHOICES
我的问题是,如果我尝试使用多个值进行过滤,但其中一个不存在,则不会获得结果。
例如,如果我要基于外键(这是外键)进行筛选,请使用2个值:
id = 2的Google公司在我的任何对象中都不存在,
公司ID为= 3的Microsoft公司确实存在,
以下内容将不返回任何结果
api/products?company=2&company=3
如果字段只是一个CharField,也会发生同样的情况。此外,使用filter_fields和filter_class时也会发生相同的行为。
起初,我实现了自己的过滤自定义get_queryset
,但是我认为使用过滤器后端可以使事情变得不那么复杂。
我不确定为什么会发生这种情况,如果我要过滤的两个值都存在,那么一切正常。
答案 0 :(得分:0)
我的问题是,如果我尝试使用多个值进行过滤,但其中不存在一个值,我将不会获得结果。
这是因为过滤是以逻辑和方式进行的。也就是说,将创建与
类似的ORM查询
Product.objects.filter(company=1).filter(company=2)
在SQL中,类似于
SELECT ... from table WHERE company=1 and company=2
一种简单的解决方案是在过滤器类中添加 lookup_expr
自变量,
class ProductFilter(filters.FilterSet):
# your code
company = filters.MultipleChoiceFilter(
field_name="company", choices=COMPANY_CHOICES,
lookup_expr='in')
如果您在视图中使用 filter_class
自变量,则不会处理filter_fields
自变量
答案 1 :(得分:0)
在您的ProductFilter
类中,您可以指定字段用于运行查询的方法。使用过滤器,您需要将company
字段的这些值组合在一起才能进行搜索。您正在做的是提供单独的值。您要做的就是将其更改为类似的内容;
api/products?company=2+3
class ProductFilter(django_filters.FilterSet):
company = django_filters.CharFilter(
label=_('Company'),
method='company_filter'
)
def company_box_filter(self, qs, name, value):
"""
Override the filter method in order to lookup for more than one field.
"""
if not value:
return qs
ids = value.split()
return qs.filter(id__in=ids)