1

I'm trying to build some kind of API, but I need to filtering request against query parameters in the URL (http:// ... / ?arg1=foo1&arg2=foo2&...). In my model scheme, I used a many to many relationships. Here is some of my code:


my_app/models.py

from django.contrib.postgres.fields import JSONField
from django.db import models


class MyData(models.Model):
    name = models.CharField(max_length=20)
    values = JSONField()


class MyModel(models.Model):
    time = models.DateTimeField()
    country = models.CharField(max_length=50)
    data = models.ManyToManyField(MyData)

my_app/serializers.py

from rest_framework import serializers
from my_app.models import MyModel, MyData


class MyDataSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyData
        fields = ('name', 'values',)


class MyModelSerializer(serializers.ModelSerializer):
    data = MyDataSerializer(many=True, read_only=True)

    class Meta:
        model = MyModel
        fields = ('country', 'data',)

my_app/views.py

from rest_framework import generics
from my_app.serializers import MySerializer
from my_app.models import MyModel


class MyView(generics.ListAPIView):
    serializer_class = MySerializer

    def get_queryset(self):
        queryset = MyModel.objects.all()
        names = self.request.query_params.get('Simon', None)
        if names:
            queryset = queryset.filter(data__name__in=names.split(','))
        return queryset

and here are the responses


Response to http://127.0.0.1:8000/hello/

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "country": "Spain",
        "data": [
            {
                "name": "Mark",
                "values": {"A": "Hello, it's Wario"}
            },
            {
                "name": "Simon",
                "values": {"A": "Hello, it's Mario"}
            },
        ]
    },

    {
        "country": "Italy",
        "data": [
            {
                "name": "Jake",
                "values": {"A": "Hello, it's Luigi"}
            }
        ]
    }
]

Response to http://127.0.0.1:8000/hello/?name=Simon

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "country": "Spain",
        "data": [
            {
                "name": "Mark",
                "values": {"A": "Hello, it's Wario"}
            },
            {
                "name": "Simon",
                "values": {"A": "Hello, it's Mario"}
            },
        ]
    }
]

But the response I wanna to obtain when I request ?name=Simon is:


Response to http://127.0.0.1:8000/hello/?name=Simon

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "country": "Spain",
        "data": [
            {
                "name": "Simon",
                "values": {"A": "Hello, it's Mario"}
            }
        ]
    }
]

2 Answers 2

3

I think we can do this with Django Filter package. To use this package you need to install it as any other python packages. pip install django-filter will do the same for you. Then you need to define a custom filter class anywhere in the project as down below,

from django_filters import rest_framework as filters
from my_app.models import MyModel

class MyFilter(filters.FilterSet):
    name = filters.CharFilter(name='data__name')

    class Meta:
        model = MyModel
        fields = ['name', ]

Then you have to change your views.py slightly as below,

from django_filters import rest_framework as filters


class MyView(generics.ListAPIView):
    serializer_class = MyModelSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = MyFilter

    def get_queryset(self):
        return MyModel.objects.all()
        



Note: I think you can define and use any kind of filter using this awesome package my customizing the filter class

Sign up to request clarification or add additional context in comments.

Comments

2

Try to use prefetch_related to filter related data:

from django.db.models import Prefetch

class MyView(generics.ListAPIView):
    serializer_class = MySerializer

    def get_queryset(self):
        queryset = MyModel.objects.all()
        names = self.request.query_params.get('Simon', None)
        if names:
            queryset = queryset.filter(data__name__in=names.split(',')).prefetch_related(Prefetch('data', queryset=MyData.objects.filter(name__in=names.split(',')))
        return queryset

Also you can use SerializerMethodField:

class MyModelSerializer(serializers.ModelSerializer):
    data = SerializerMethodField()

    class Meta:
        model = MyModel
        fields = ('country', 'data',)

    def get_data(self, obj):
        names = self.context['request'].query_params.get('Simon', None)
        data = MyData.objects.filter(name__in=names.split(','))
        data_serializer = MyDataSerializer(data, many=True)
        return data_serializer.data

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.