我正在使用django-filter和django-rest-framework,我正在尝试实例化一个过滤器,该过滤器接受用于过滤查询集的数字列表
class MyFilter(django_filters.FilterSet):
ids = django_filters.NumberFilter(name='id',lookup_type='in')
class Meta:
model = MyModel
fields = ('ids',)
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
filter_class = MyFilter
如果我传入逗号分隔的整数列表,则完全忽略过滤器。
如果我传入一个整数,它会通过django-filter进入django的表单验证器并抱怨:
'Decimal' object is not iterable
有没有办法创建一个django-filter对象,它可以处理整数列表并正确过滤掉查询集?
答案 0 :(得分:24)
无论好坏,我为此创建了一个自定义过滤器:
class IntegerListFilter(django_filters.Filter):
def filter(self,qs,value):
if value not in (None,''):
integers = [int(v) for v in value.split(',')]
return qs.filter(**{'%s__%s'%(self.name, self.lookup_type):integers})
return qs
使用方法如下:
class MyFilter(django_filters.FilterSet):
ids = IntegerListFilter(name='id',lookup_type='in')
class Meta:
model = MyModel
fields = ('ids',)
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
filter_class = MyFilter
现在我的界面接受以逗号分隔的整数列表。
答案 1 :(得分:6)
这是一个完整的解决方案:
from django_filters import Filter, FilterSet
from rest_framework.filters import DjangoFilterBackend
from rest_framework.viewsets import ModelViewSet
from .models import User
from .serializers import UserSerializer
class ListFilter(Filter):
def filter(self, qs, value):
if not value:
return qs
self.lookup_type = 'in'
values = value.split(',')
return super(ListFilter, self).filter(qs, values)
class UserFilter(FilterSet):
ids = ListFilter(name='id')
class Meta:
model = User
fields = ['ids']
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
filter_backends = (DjangoFilterBackend,)
filter_class = UserFilter
答案 2 :(得分:6)
我知道这是一个老帖子,但现在有一个更好的解决方案。使其正确的更改已发布here。
他们添加了BaseInFilter
和BaseRangeFilter
。文档为here。
大图,BaseFilter检查CSV,然后当与另一个过滤器混合时,它会按照您的要求进行操作。您的代码现在可以写成:
class NumberInFilter(filters.BaseInFilter, filters.NumberFilter):
pass
class MyModelViewSet(viewsets.ModelViewSet):
ids = NumberInFilter(name='id', lookup_expr='in')
class Meta:
model = MyModel
fields = ['ids']
答案 3 :(得分:4)
根据django-filter issues中的帖子:
from django_filters import Filter
from django_filters.fields import Lookup
class ListFilter(Filter):
def filter(self, qs, value):
return super(ListFilter, self).filter(qs, Lookup(value.split(u","), "in"))
我亲自在我的项目中使用了这个,没有任何问题,而且无需创建每个类型的过滤器。
答案 4 :(得分:1)
根据@yndolok的回答,我得出了一个通用的解决方案。我认为通过id列表进行过滤是一项非常常见的任务,因此应该包含在FilterBackend中:
class ListFilter(django_filters.Filter):
"""Class to filter from list of integers."""
def filter(self, qs, value):
"""Filter function."""
if not value:
return qs
self.lookup_type = 'in'
try:
map(int, value.split(','))
return super(ListFilter, self).filter(qs, value.split(','))
except ValueError:
return super(ListFilter, self).filter(qs, [None])
class FilterBackend(filters.DjangoFilterBackend):
"""A filter backend that includes ListFilter."""
def get_filter_class(self, view, queryset=None):
"""Append ListFilter to AutoFilterSet."""
filter_fields = getattr(view, 'filter_fields', None)
if filter_fields:
class AutoFilterSet(self.default_filter_set):
ids = ListFilter(name='id')
class Meta:
model = queryset.model
fields = list(filter_fields) + ["ids"]
return AutoFilterSet
else:
return super(FilterBackend, self).get_filter_class(view, queryset)
答案 5 :(得分:0)
最新解决方案:
从django_filters导入rest_framework作为过滤器
名称-> field_name
lookup_type-> lookup_expr
class IntegerListFilter(filters.Filter):
def filter(self,qs,value):
if value not in (None,''):
integers = [int(v) for v in value.split(',')]
return qs.filter(**{'%s__%s'%(self.field_name, self.lookup_expr):integers})
return qs
class MyFilter(filters.FilterSet):
ids = IntegerListFilter(field_name='id',lookup_expr='in')
class Meta:
model = MyModel
fields = ('ids',)
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
filter_class = MyFilter
答案 6 :(得分:0)
正如我在这里DjangoFilterBackend with multiple ids所回答的那样,现在很容易制作一个可以接受列表并验证内容的过滤器
例如:
from django_filters import rest_framework as filters
class NumberInFilter(filters.BaseInFilter, filters.NumberFilter):
pass
class MyFilter(filters.FilterSet):
id_in = NumberInFilter(field_name='id', lookup_expr='in')
class Meta:
model = MyModel
fields = ['id_in', ]
这将接受来自get参数的整数列表。例如/endpoint/?id_in=1,2,3