使用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']
错误原因
- 视图中错误实例化FilterSet:
filterset_class要求赋值的是类本身,而不是类的实例。你这里直接写ProductFilter(category_slug=get_category),相当于提前实例化了FilterSet,Django后续尝试调用这个实例时就会报“object is not callable”错误。 - FilterSet的__init__方法未传递参数给父类:调用父类
__init__时没传*args和**kwargs,会导致FilterSet初始化不完整。 - 实例方法调用错误:
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
相关产品推荐
相关产品推荐

