Search code examples
pythondjangodjango-rest-frameworkdjango-filter

django-rest-framework- Filtering using 'or' on multiple values from one url parameter


I have a tagging system in place for a model that my API exposes. The models look something like this:

class TaggableModel(models.Model):
    name = models.CharField(max_length=255)
    tags = models.ManyToManyField(Tag, related_name="taggable_models")

class Tag(models.Model):
    tag = models.CharField(max_length=32)

I've then set up a serializer and view that look like:

class TaggableModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = TaggableModel
        fields = ('id', 'name', 'tags',)
        read_only_fields = ('id',)

class TaggableModelViewSet(viewsets.ModelViewSet):
    queryset = TaggableModel.objects.all()
    serializer_class = TaggableModelSerializer
    permission_classes = (AllowAny,)
    filter_backend = [DjangoFilterBackend]
    filterset_fields = ['tags']

If I want to grab all TaggableModels that have tag ids 1, 2, or 3, I can do so via:

https://my-api-domain/api/taggable-models?tags=1&tags=2&tags=3

Is there a way to split on a delimiter, so I can pass it all as one parameter? e.g.:

https://my-api-domain/api/taggable-models?tags=1,2,3

It looks like I can write my own custom DjangoFilterBackend filters, but I am a bit unsure as to where to start. Or perhaps there is an easier way to accomplish this?


Solution

  • Sure you can do this by having custom filterset class with specific field 'widget' (that's how it is called in django-filters)

    Here's a sample you can try:

    # filters.py
    
    from django_filters.rest_framework import FilterSet, filters
    from django_filters.widgets import CSVWidget
    
    from .your_models import Tag, TaggableModel
    
    class TaggableModelFilterSet(FilterSet):
        tags = filters.ModelMultipleChoiceFilter(
            queryset=Tag.objects.all(), widget=CSVWidget,
            help_text=_("A list of ids, comma separated, identifying tags"),
            method='filter_tags'
        )
    
        class Meta:
            model = TaggableModel
            fields = ['tags']
    
        def filter_tags(self, queryset, name, value):
            if value:
                queryset = queryset.filter(tags__in=value)
            return queryset
    
    # views.py
    
    class TaggableModelViewSet(viewsets.ModelViewSet):
        queryset = TaggableModel.objects.all()
        serializer_class = TaggableModelSerializer
        permission_classes = (AllowAny,)
        filter_backends = [DjangoFilterBackend]
        filter_class = TaggableModelFilterSet