具有 collated/merged 个值的查询集
Queryset with collated/merged values
假设我有以下两个模型:
class Parent(models.Model):
name = models.CharField(max_length=48)
class Child(models.Model):
name = models.CharField(max_length=48)
movement = models.ForeignKey(Parent, related_name='children')
我有以下 DRF generics.ListAPIView
,我希望能够在 Child
对象上 search/filter 但实际上 return 相关的 Parent
对象:
class ParentSearchByChildNameView(generics.ListAPIView):
"""
Return a list of parents who have a child with the given name
"""
serializer_class = ParentSerializer
def get_queryset(self):
child_name = self.request.query_params.get('name')
queryset = Child.objects.all()
if child_name is not None:
queryset = queryset.filter(name__contains=child_name)
matched_parents = Parent.objects.filter(children__in=queryset).distinct()
return matched_parents
现在,这对我来说效果很好。如果一个 Parent
对象有 3 个 Child
对象,它们都匹配给定的“名称”query_param
,那么我只得到一个 Parent
对象,这就是我想要的:
{
"next": null,
"previous": null,
"results": [
{
"url": "URL to parent",
"name": "Parent #1",
}
]
}
但是,我还想要在结果中指示匹配的 Child
对象 ID。如果我可以在 JSON 中说明我想要什么:
{
"next": null,
"previous": null,
"results": [
{
"url": "URL to parent",
"name": "Parent #1",
"matched_child": [1, 3, 7]
}
]
}
这是我可以使用内置工具完成的事情,而无需昂贵且反复访问数据库吗?
如果您与 postgresql, you can work with an ArrayAgg
expression [Django-doc] 一起工作:
from django.contrib.postgres.aggregates import <b>ArrayAgg</b>
class ParentSearchByChildNameView(generics.ListAPIView):
"""
Return a list of parents who have a child with the given name
"""
serializer_class = ParentSerializer
def get_queryset(self):
child_name = self.request.query_params.get('name')
return = Parent.objects.filter(
children__name__contains=child_name
).annotate(<strong>matched_children=ArrayAgg('children__pk')</strong>)
在序列化程序中,您因此添加了一个 ListField
,它将列出子项:
class ParentSerializer(serializers.ModelSerializer)
<strong>matched_children</strong> = serializers.ListField(
child=serializers.IntegerField(),
read_only=True
)
class Meta:
model = Parent
fields = ['url', 'name', <strong>'matched_children'</strong>]
在不使用特定于数据库的功能的情况下,您可以使用 Prefetch
object [Django-doc]:
from django.db.models import Prefetch
class ParentSearchByChildNameView(generics.ListAPIView):
"""
Return a list of parents who have a child with the given name
"""
serializer_class = ParentSerializer
def get_queryset(self):
child_name = self.request.query_params.get('name')
return = Parent.objects.filter(
children__name__contains=child_name
).prefetch_related(
<strong>Prefetch('children', Child.objects.filter(name=child_name), to_attr='matched_children')</strong>
).distinct()
对于序列化程序,我们可以使用 PrimaryKeyRelatedField
[drf-doc]:
class ParentSerializer(serializers.ModelSerializer)
matched_children = serializers.<strong>PrimaryKeyRelatedField(</strong>
many=True,
read_only=True
<strong>)</strong>
class Meta:
model = Parent
fields = ['url', 'name', <strong>'matched_children'</strong>]
假设我有以下两个模型:
class Parent(models.Model):
name = models.CharField(max_length=48)
class Child(models.Model):
name = models.CharField(max_length=48)
movement = models.ForeignKey(Parent, related_name='children')
我有以下 DRF generics.ListAPIView
,我希望能够在 Child
对象上 search/filter 但实际上 return 相关的 Parent
对象:
class ParentSearchByChildNameView(generics.ListAPIView):
"""
Return a list of parents who have a child with the given name
"""
serializer_class = ParentSerializer
def get_queryset(self):
child_name = self.request.query_params.get('name')
queryset = Child.objects.all()
if child_name is not None:
queryset = queryset.filter(name__contains=child_name)
matched_parents = Parent.objects.filter(children__in=queryset).distinct()
return matched_parents
现在,这对我来说效果很好。如果一个 Parent
对象有 3 个 Child
对象,它们都匹配给定的“名称”query_param
,那么我只得到一个 Parent
对象,这就是我想要的:
{
"next": null,
"previous": null,
"results": [
{
"url": "URL to parent",
"name": "Parent #1",
}
]
}
但是,我还想要在结果中指示匹配的 Child
对象 ID。如果我可以在 JSON 中说明我想要什么:
{
"next": null,
"previous": null,
"results": [
{
"url": "URL to parent",
"name": "Parent #1",
"matched_child": [1, 3, 7]
}
]
}
这是我可以使用内置工具完成的事情,而无需昂贵且反复访问数据库吗?
如果您与 postgresql, you can work with an ArrayAgg
expression [Django-doc] 一起工作:
from django.contrib.postgres.aggregates import <b>ArrayAgg</b>
class ParentSearchByChildNameView(generics.ListAPIView):
"""
Return a list of parents who have a child with the given name
"""
serializer_class = ParentSerializer
def get_queryset(self):
child_name = self.request.query_params.get('name')
return = Parent.objects.filter(
children__name__contains=child_name
).annotate(<strong>matched_children=ArrayAgg('children__pk')</strong>)
在序列化程序中,您因此添加了一个 ListField
,它将列出子项:
class ParentSerializer(serializers.ModelSerializer)
<strong>matched_children</strong> = serializers.ListField(
child=serializers.IntegerField(),
read_only=True
)
class Meta:
model = Parent
fields = ['url', 'name', <strong>'matched_children'</strong>]
在不使用特定于数据库的功能的情况下,您可以使用 Prefetch
object [Django-doc]:
from django.db.models import Prefetch
class ParentSearchByChildNameView(generics.ListAPIView):
"""
Return a list of parents who have a child with the given name
"""
serializer_class = ParentSerializer
def get_queryset(self):
child_name = self.request.query_params.get('name')
return = Parent.objects.filter(
children__name__contains=child_name
).prefetch_related(
<strong>Prefetch('children', Child.objects.filter(name=child_name), to_attr='matched_children')</strong>
).distinct()
对于序列化程序,我们可以使用 PrimaryKeyRelatedField
[drf-doc]:
class ParentSerializer(serializers.ModelSerializer)
matched_children = serializers.<strong>PrimaryKeyRelatedField(</strong>
many=True,
read_only=True
<strong>)</strong>
class Meta:
model = Parent
fields = ['url', 'name', <strong>'matched_children'</strong>]