具有排序规则/合并值的查询集



假设我有以下两个模型:

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

我有以下 DRFgenerics.ListAPIView,我希望能够搜索/过滤Child对象,但实际上返回相关的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]
}
]
}

这是我可以使用内置工具做的事情,而无需昂贵且反复访问数据库吗?

在不使用特定于数据库的功能的情况下,您可以使用Prefetch对象 [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(
Prefetch('children', Child.objects.filter(name=child_name), to_attr='matched_children')
).distinct()

对于序列化程序,我们可以使用PrimaryKeyRelatedField[drf-doc]:

class ParentSerializer(serializers.ModelSerializer)
matched_children = serializers.PrimaryKeyRelatedField(
many=True,
read_only=True
)
class Meta:
model = Parent
fields = ['url', 'name','matched_children']

如果你使用postgresql,你可以使用ArrayAgg表达式 [Django-doc]:

from django.contrib.postgres.aggregates importArrayAgg
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(matched_children=ArrayAgg('children__pk'))

因此,在序列化程序中,您将添加一个ListField,该将列出具有以下条件的子项:

class ParentSerializer(serializers.ModelSerializer)
matched_children= serializers.ListField(
child=serializers.IntegerField(),
read_only=True
)
class Meta:
model = Parent
fields = ['url', 'name','matched_children']

最新更新