Django REST Framework 中的查询参数过滤,多对多关系

1 投票
2 回答
3777 浏览
提问于 2025-05-18 21:18

我正在尝试构建某种API,但我需要根据URL中的查询参数来过滤请求(比如:http:// ... / ?arg1=foo1&arg2=foo2&...)。在我的模型设计中,我使用了多对多的关系。以下是我的一些代码:


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

这里是一些响应示例:


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"}
            }
        ]
    }
]

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"}
            },
        ]
    }
]

但是我希望在请求 ?name=Simon 时得到的响应是:


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 个回答

2

试着使用 prefetch_related 来过滤相关的数据:

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

你还可以使用 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
3

我觉得我们可以用 Django Filter 这个包来实现这个功能。要使用这个包,你需要像安装其他Python包一样进行安装。只需运行 pip install django-filter 就可以了。接下来,你需要在项目中的某个地方定义一个自定义的过滤器类,像下面这样,

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', ]

然后你需要稍微修改一下你的 views.py 文件,像下面这样,

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()
        



注意:我觉得你可以通过自定义 filter class 来定义和使用任何类型的过滤器,这个包真的很棒。

撰写回答