Diesel
Diesel

Reputation: 5335

Django-Filter and query with an array

I have a simple question: Currently I can do this to get an object from my backend:

http://127.0.0.1:8000/api/v1/boats/boats?id=10
http://127.0.0.1:8000/api/v1/boats/boats?home_port=98&id=5

But I'd like to get an array of boats based on a list of ids or a list of home_ports, and I have tried:

http://127.0.0.1:8000/api/v1/boats/boats?id=10,11
http://127.0.0.1:8000/api/v1/boats/boats?id_in=10,11
http://127.0.0.1:8000/api/v1/boats/boats?id=10,id=11
http://127.0.0.1:8000/api/v1/boats/boats?id=10&id=11

But these do not work. What is the best way to do this with django-filter, how are the URL rules defined?

Here is my view:

class BoatList(generics.ListCreateAPIView):
    permission_classes = (IsOwnerOrReadOnly,)
    serializer_class = BoatSerializer
    queryset = Boat.objects.all()
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('id', 'home_port',)

The solution I marked accepted 100% answers my question, but I ended up implementing something different based on another post I found which uses filters:

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 BoatFilter(FilterSet):
    ids = ListFilter(name='id')

    class Meta:
        model = Boat
        fields = ['home_port', 'ids']


class BoatList(generics.ListCreateAPIView):
    permission_classes = (IsOwnerOrReadOnly,)
    serializer_class = BoatSerializer
    queryset = Boat.objects.all()
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = BoatFilter

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

Upvotes: 9

Views: 13645

Answers (4)

Gautam Goyal
Gautam Goyal

Reputation: 1

Simple Solution Make a object of your filterset fields if you want to use an array.

filterset_fields = {'id':['exact'],'dist':['exact'],'data_type':['exact','in'], 'data_sub_type':['exact','in']}

Lets see data_sub_type now if from postman we can send a list. data_type__in = "1,2,3" like this

Upvotes: 0

zhibeen
zhibeen

Reputation: 71

you just creat a filter class

    class NumberInFilter(BaseInFilter, NumberFilter):
          pass


    class myFilter(FilterSet):
          id__in = NumberInFilter(field_name='id', lookup_expr='in')

          class Meta:
                model = Boat

in the viewset use the

           filter_class = myFilter

you can see this document of the django-filter: https://django-filter.readthedocs.io/en/master/ref/filters.html?highlight=BaseInFilter

Upvotes: 4

Animesh Sharma
Animesh Sharma

Reputation: 3386

The answer given by doniyor is quite apt. But I guess request won't be available where it is used.

There is another way of doing this.You can override the get_queryset method. This can be done as follows:

class BoatList(generics.ListCreateAPIView):
    permission_classes = (IsOwnerOrReadOnly,)
    serializer_class = BoatSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('id', 'home_port',)

    def get_queryset(self):
        id_list = self.request.GET.getlist("id")
        if not id_list:
            return []
        return Boat.objects.filter(id__in=id_list)

Upvotes: 8

doniyor
doniyor

Reputation: 37846

try this:

url: http://127.0.0.1:8000/api/v1/boats/boats?id=10,11

class BoatList(generics.ListCreateAPIView):
    permission_classes = (IsOwnerOrReadOnly,)
    serializer_class = BoatSerializer
    queryset = Boat.objects.filter(id__in=request.GET.getlist('id')) #<------
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('id', 'home_port',)

Upvotes: 3

Related Questions