Можно сделать `in` oodup_type`, через диабаркер Django-фильтр URL?

StackOverflow https://stackoverflow.com//questions/24041639

Вопрос

Я использую django-фильтр с 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-фильтр в валидатор формы Django и жалуется:

'Decimal' object is not iterable
.

Есть ли способ создать объект 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
.

Теперь мой интерфейс принимает коммерческие списки целых чисел.

Другие советы

Я знаю, что это старый пост, но теперь есть лучшее решение.Изменение, которое делает его правильным, размещена Здесь .

Они добавили генеракодицетагкод и генеракодицетагкод.Документация является здесь .P >.

Большая картина, чеки 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top