Django-Filter URL 파서를 통해`in 'lookup_type`을 수행 할 수 있습니까?
-
21-12-2019 - |
문제
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 객체를 만들고 queryset을 올바르게 필터링 할 수있는 Django 필터 객체를 만드는 방법이 있습니까?
해결책
더 좋거나 더 나쁜 것은 사용자 정의 필터를 만들었습니다.
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
.
이제 내 인터페이스는 쉼표로 구분 된 정수 목록을 허용합니다.
다른 팁
나는 이것이 오래된 게시물이라는 것을 알고 있지만, 이제는 더 나은 해결책이있다.올바른 변화는 여기에 게시됩니다.
BaseInFilter
와 BaseRangeFilter
를 추가했습니다.이 문서는 여기
큰 그림, 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']
. 여기에 완전한 해결책이 있습니다 :
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
. According to a post in the 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"))
I have personally used this without any issue in my projects, and it works without having to create a per-type filter.
Based on @yndolok answer I have come to a general solution. I think filtering by a list of ids is a very common task and therefore should be included in the 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)
Uptodate solution:
from django_filters import rest_framework as filters
name-->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
As I have answered here DjangoFilterBackend with multiple ids, it is now pretty easy to make a filter that accepts list and validates the contents
For Example:
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', ]
This will accept a list of integers from a get parameter. For example /endpoint/?id_in=1,2,3