You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DjangoFilterBackend自定义FilterSet报错:'ProductFilter'对象不可调用

问题:使用DjangoFilterBackend结合自定义FilterSet时出现'ProductFilter' object is not callable错误

我要实现根据商品类别筛选对应属性的商品,结合DjangoFilterBackend和自定义FilterSet类。不限制属性选项时筛选正常,但添加类别限定后,出现上述错误。

相关代码

模型代码

class Category(models.Model):
    title = models.CharField(max_length=50)
    desc = models.TextField(default='', blank=True)
    slug = models.SlugField()


class ProductEnumProperty(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='product_enum_properties')
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name


class ProductPropertyState(models.Model):
    property = models.ForeignKey(ProductEnumProperty, on_delete=models.CASCADE, related_name='product_property_states')
    state = models.CharField(max_length=20)

    def __str__(self):
        return f'{self.property.name} - {self.state}'


class Product(models.Model):
    title = models.CharField(max_length=50)
    slug = models.SlugField()
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='products')
    desc = models.TextField(blank=True, null=True)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    date_created = models.DateTimeField(auto_now_add=True)
    props = models.ManyToManyField(ProductPropertyState)

示例关系

类别:jeans → ProductEnumProperty:flyer type → ProductPropertyStates:buttons、zipper

原filters.py代码

class ProductFilter(FilterSet):
    class Meta:
        model = Product
        fields = {
            'price': ['gt', 'lt'],
            'props': ['exact'],
        }

    def __init__(self, *args, **kwargs):
        category_slug = kwargs['category_slug']
        del kwargs['category_slug']
        super(ProductFilter, self).__init__()
        self.filters['props'].extra.update(
            {'queryset': ProductPropertyState.objects.filter(property__category__slug=category_slug)})

原views.py代码

class ProductByCategory(ListAPIView):
    def get_category(self):
        category_slug = self.kwargs.get('category_slug')
        return category_slug

    serializer_class = ProductMiniSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ProductFilter(category_slug=get_category)
    search_fields = ['title', 'desc', 'category__title']
    ordering_fields = ['price', 'title']

错误原因

  1. 视图中错误实例化FilterSet:filterset_class要求赋值的是类本身,而不是类的实例。你这里直接写ProductFilter(category_slug=get_category),相当于提前实例化了FilterSet,Django后续尝试调用这个实例时就会报“object is not callable”错误。
  2. FilterSet的__init__方法未传递参数给父类:调用父类__init__时没传*args和**kwargs,会导致FilterSet初始化不完整。
  3. 实例方法调用错误:get_category是视图类的实例方法,直接通过get_category调用会报错,因为它需要绑定到视图实例才能访问self.kwargs。

解决方案

1. 修正FilterSet的__init__方法

确保父类初始化时接收所有参数:

# filters.py
class ProductFilter(FilterSet):
    class Meta:
        model = Product
        fields = {
            'price': ['gt', 'lt'],
            'props': ['exact'],
        }

    def __init__(self, *args, **kwargs):
        # 取出category_slug参数
        category_slug = kwargs.pop('category_slug')
        # 传递剩余参数给父类初始化
        super().__init__(*args, **kwargs)
        # 更新props筛选器的queryset
        self.filters['props'].extra.update(
            {'queryset': ProductPropertyState.objects.filter(property__category__slug=category_slug)}
        )

2. 在视图中动态传递参数给FilterSet

推荐用重写get_filterset_kwargs的方式,更贴合Django的扩展规范:

# views.py
class ProductByCategory(ListAPIView):
    serializer_class = ProductMiniSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    # 这里只指定FilterSet类,不要实例化
    filterset_class = ProductFilter
    search_fields = ['title', 'desc', 'category__title']
    ordering_fields = ['price', 'title']

    def get_filterset_kwargs(self, filterset_class):
        kwargs = super().get_filterset_kwargs(filterset_class)
        # 添加category_slug到初始化参数中
        kwargs['category_slug'] = self.kwargs.get('category_slug')
        return kwargs

也可以选择重写get_filterset直接实例化FilterSet,更直观:

# views.py 另一种写法
class ProductByCategory(ListAPIView):
    serializer_class = ProductMiniSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ProductFilter
    search_fields = ['title', 'desc', 'category__title']
    ordering_fields = ['price', 'title']

    def get_filterset(self, filterset_class):
        # 从URL参数中获取category_slug
        category_slug = self.kwargs.get('category_slug')
        # 实例化FilterSet并传入参数
        return filterset_class(
            data=self.request.query_params,
            queryset=self.get_queryset(),
            request=self.request,
            category_slug=category_slug
        )

内容的提问来源于stack exchange,提问作者Delam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:15:43