Django REST Framework 中的查询参数过滤,多对多关系
我正在尝试构建某种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
来定义和使用任何类型的过滤器,这个包真的很棒。