[Fixed]-How to filter for multiple ids from a query param on a GET request with django rest framework?

17👍

Based in your comment, you could send the ids via url:

127.0.0.1:8000/snippets/?ids=2,3,4

and in your view

...
ids = request.GET.get('ids')  # u'2,3,4' <- this is unicode
ids = ids.split(',')  # [u'2',u'3',u'4'] <- this is a list of unicodes with ids values

Then you can query to Snippet model:

Snippet.objects.filter(pk__in=ids)

This could give you some problems if there’s spaces between ids in url:

127.0.0.1:8000/snippets/?ids=2, 3 , 4

You could need process every value before perform a query

👤Gocht

11👍

I found this to work, following the Django REST Framework main tutorial and then documentation on Filtering against query parameters, adapting slightly. This allows a single url to return data from two GET requests: one returning objects whose ids match those given as a parameter, the other returning all objects, when no parameters are provided.

snippets/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
   .... (code for other urls here)
   url(r'^snippets/$', views.SnippetList.as_view(), name='snippet-list'),
   ....
]

snippets/views.py

.... 
from snippet.serializers import SnippetSerializer
....

class SnippetList(generics.ListCreateAPIView):
    serializer_class = SnippetSerializer

    def get_queryset(self):

        # Get URL parameter as a string, if exists 
        ids = self.request.query_params.get('ids', None)

        # Get snippets for ids if they exist
        if ids is not None:
            # Convert parameter string to list of integers
            ids = [ int(x) for x in ids.split(',') ]
            # Get objects for all parameter ids 
            queryset = Product.objects.filter(pk__in=ids)

        else:
            # Else no parameters, return all objects
            queryset = Product.objects.all()

        return queryset

snippets/serializers.py

....
class SnippetSerializer(serializers.ModelSerializer):

    class Meta:
        model = Snippet
        fields = ('url', 'id', 'title', 'code', 'linenos', 'language', 'style')

7👍

You can use django-filter and set it up as a filter and avoid over riding the get_queryset method on the viewset

Given this request

/api/snippets/?ids=1,2,3,4

Then write a django filter set and method

import django_filters


def filter_by_ids(queryset, name, value):
    values = value.split(',')
    return queryset.filter(id__in=values)


class SnippetFilterSet(django_filters.FilterSet):
   ids = django_filters.CharFilter(method=filter_by_ids)
    
   class Meta:
      model = Snippet
      fields = ['ids']

And then in your ModelViewSet

from rest_framework.viewsets import ModelViewSet
from app.snippets.models import Snippet
from app.snippets.filters import SnippetFilterSet # path to filterset

class SnippetView(ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    filterset_class = SnippetFilterSet
👤Dap

3👍

A possible way is to send the lis of pk(s) as GET request data, somthing like this:

  • GET request to “/snippets”

  • Request body: {“list_of_pk”: [1,2,3…]}

And then:

snippets/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

snippets/views.py

def snippet_list(request):
    if request.method == 'GET':
        pk_list = request.GET.get('list_of_pk')
        if pk_list:
            snippets = Snippet.objects.filter(pk__in=pk_list)
        else:
            snippets = Snippet.objects.all()
        #the serialization...

3👍

Here’s what I ended up going with:

No changes to
snippet/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

http://127.0.0.1:8000/snippets/?ids=2,3,4 is received by

snippet/views.py

from rest_framework.decorators import api_view

@api_view(['GET', 'POST'])    
def snippet_list(request):
    if request.method == 'GET':
        ids = request.query_params.get('ids')  # u'2,3,4' <- this is unicode
        ids = ids.split(',')
        snippets = Snippet.objects.filter(pk__in=ids)
        serializer = SnippetSerializer(snippet, many=True)
        return JSONResponse(serializer.data)

0👍

While there is no definitive standard, most web frameworks allow multiple values to be associated with a single field (e.g. field1=value1&field1=value2&field2=value3).

Django also supports this.
To get list of values you can use: ids = request.GET.getlist('ids').
More examples from MultiValueDict docs:

    >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
    >>> d['name']
    'Simon'
    >>> d.getlist('name')
    ['Adrian', 'Simon']
    >>> d.getlist('doesnotexist')
    []
    >>> d.getlist('doesnotexist', ['Adrian', 'Simon'])
    ['Adrian', 'Simon']
    >>> d.get('lastname', 'nonexistent')
    'nonexistent'
    >>> d.setlist('lastname', ['Holovaty', 'Willison'])
👤ambi

0👍

The django-filter documentation give a good example how to do it: https://django-filter.readthedocs.io/en/stable/ref/filters.html?highlight=ids#baseinfilter

For your case:

class SnippetFilterSet(BaseInFilter, NumberFilter):
    pass

class F(FilterSet):
    ids = NumberInFilter(field_name='id', lookup_expr='in')

    class Meta:
        model = Snippet
👤Slava

Leave a comment