Search code examples
djangodjango-rest-frameworkdjango-filterdjango-filters

How do I use multi tag filters in Django REST Framework API response?


first of all I apologize if I did not ask a question using better words. I am building a DRF App, I have Products Model, ProductSerializer and ProductsSummaryView. Furthermore, I have Workspace Model (not going to talk about it as it is not related to the issue). Now, I want to return those products details in success response which are attached with a specific Workspace, filtered on some parameters.

What I have done: I defined filterset_fields and filter_backends. It works fine. For example, if I go to this URL {{domain}}/path/?country__title=Japan, it filters the data for Japan and show me. But the problem is, I want to filter using multi-tag, such as let's say if I want to see details for Japan and Korea, so I want something like this {{domain}}/path/?country__title=Japan&country__title=Korea, but it is not working and it just returns all the details. I even tried adding empty list format in the URL and tried this URL {{domain}}/path/?country__title[]=Japan&country__title[]=Korea, but it still did not work.

Can anybody help me in this?

My Product Model is:

class Product(BaseModel):
    """Product model to store all prices of a product and
    related brand and countries it's available in"""
    product = models.CharField(max_length=255)
    country = models.ForeignKey(Country, null=True, on_delete=models.DO_NOTHING)
    fzg_segment = models.ForeignKey(
        FZGSegment, on_delete=models.DO_NOTHING, null=True, blank=True
    )

My ProductSerializer Serializer is:

class ProductSerializer(serializers.ModelSerializer):
    """Serializer for the part object"""
    fzg_segment = FZGSegmentSerializer()

    class Meta:
        model = Product
        fields = (
            "id",
            "product",
            "fzg_segment",
        )
        read_only_fields = ("id",)

And my ProductsSummaryView API is:

class ProductsSummaryView(mixins.ListModelMixin, viewsets.GenericViewSet):
    """Viewset to return data for products in a workspace"""
    serializer_class = serializers.ProductSerializer
    permission_classes = (permissions.IsWorkspaceAdminPermission,)
    pagination_class = StandardSetPagination
    filter_backends = [DjangoFilterBackend]
    filterset_fields = [
        "fzg_segment__title",
        "country__title",
    ]

    def get_queryset(self) -> models.Product:
        """Returns queryset of all the products of a workspace"""
        workspace = self.kwargs["workspace"]
        workspace = get_object_or_404(models.Workspace, pk=workspace)
        return workspace.products.all()

Can anyone help me and let me know what do I need to do in order to achieve desired functionality?

UPDATE: I have created filters.py file as below:

# filters.py

import django_filters
from .models import Product

class ProductFilter(django_filters.FilterSet):
    fzg_segment__title = django_filters.CharFilter(field_name="fzg_segment__title", lookup_expr='in')
    country__title = django_filters.CharFilter(field_name="country__title", lookup_expr='in')

    class Meta:
        model = Product
        fields = [
            "fzg_segment__title",
            "country__title",
        ]

and I have update my views.py as below:

# views.py

class ProductsSummaryView(mixins.ListModelMixin, viewsets.GenericViewSet):
    """Viewset to return data for products in a workspace"""
    serializer_class = serializers.ProductSerializer
    permission_classes = (permissions.IsWorkspaceAdminPermission,)
    pagination_class = StandardSetPagination
    filter_backends = [DjangoFilterBackend]
    filterset_class = filters.ProductFilter

    def get_queryset(self) -> models.Product:
        """Returns queryset of all the products of a workspace"""
        workspace = self.kwargs["workspace"]
        workspace = get_object_or_404(models.Workspace, pk=workspace)
        queryset = workspace.products.all()
        queryset = self.filter_queryset(queryset)
        return queryset

and I tried to use filters as following in the URL:

{{domain]]/path/?country__title=Japan&country__title=Korea

and

`{{domain]]/path/?country__title__in=Japan&country__title__in=Korea``

and

{{domain]]/path/?country__title__in=Japan,Korea

and

{{domain]]/path/?country__title[]=Japan&country__title[]=Korea

But all in vain, I am unable to use even single filter, leave the multiple one. What is wrong am I doing?


Solution

  • You need to use BaseInFilter, example:

    from django_filters.filters import BaseInFilter
    from django_filters import rest_framework as filters
    
    class CharInFilter(BaseInFilter, filters.CharFilter):
        pass
    
    
    class ProductFilterSet(filters.FilterSet):
        country = CharInFilter(field_name="country__title", lookup_expr='in')
    
        class Meta:
            model = Product
            fields = ["country"]
        
    

    The params url like: /?country=Japan,Korea