为整个结果集向Django Rest Framework结果添加额外数据

43 投票
5 回答
19965 浏览
提问于 2025-04-18 09:25

我正在使用Django Rest Framework,想要在结果集中添加一些额外的数据。通常情况下,你会看到这样的内容:

{
    "count": 45, 
    "next": "http://localhost:8000/foo/bar?page=2", 
    "previous": null, 
    "results": [
        {...}
    ]
}

我想要像这样添加额外的计数:

{
    "count": 45,
    "10_mi_count": 10,
    "20_mi_count": 30,
    "30_mi_count": 45,
    "next": "http://localhost:8000/foo/bar?page=2", 
    "previous": null, 
    "results": [
        {...}
    ]
}

在这个例子中,额外的计数就是指有多少个对象的“距离”这个字段的值小于关键字中描述的英里数。

我的问题是,我不知道在哪里扩展和插入这个功能才是最合适的。

理想情况下,我希望这个功能在结果是否分页的情况下都能正常工作,不想做任何假设。

我真正想要的是一个正确的方向指引(以及为什么这个地方是合适的)。

我查阅了文档,但找不到任何描述如何添加这些内容的信息,不过我很乐意被证明我错了。

5 个回答

2

我也使用了DRF(Django REST框架)和分页功能,并想要添加一个返回的查询集的总结。所以我首先需要在列表方法中获取这个查询集。

def list(self, request, *args, **kwargs):
    queryset = self.filter_queryset(self.get_queryset())
    summary = {}
    summary['x'] = queryset.filter(transaction_type='x').count()
    summary['y'] = queryset.filter(transaction_type='y').count()

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        data = {}
        data['summary'] = summary
        data['details'] = serializer.data
        return self.get_paginated_response(data)


    serializer = self.get_serializer(queryset, many=True)
    data = {}
    data['summary'] = summary
    data['details'] = serializer.data
    return Response(data)

这样我得到了以下的响应:

{
  "count": 20,
  "next": "http://..",
  "previous": null,
  "results": {
    "summary": {
      "x": 15,
      "y": 5,
    },
    "details": [
      {.....

注意:这也适用于没有分页的结果。

3

我在这里借用一下被接受的答案!

我觉得主要的问题是,序列化器返回的是一个ReturnList,这其实就是一个普通的列表,只是多了一些额外的信息。

你可以像下面这样,把数据替换成你自己的OrderedDict:

response = super().list(request, *args, **kwargs)
new_data = OrderedDict()
new_data.update({
    'results': response.data,
    '10-mi-count': 10,
    #etc...
})
response.data = new_data

return response 
5

最后,我创建了一个自定义的分页序列化器,里面有一个字段,像这样:

class DistanceCountField(serializers.Field):
    def to_native(self, value):
        try:
            distance_counts = {
                '1_mile': self._count_lte_miles(value, 1),
                '5_mile': self._count_lte_miles(value, 5),
                '10_mile': self._count_lte_miles(value, 10),
                '20_mile': self._count_lte_miles(value, 20),
            }
        except FieldError:
            distance_counts = None

        return distance_counts

    def _count_lte_miles(self, value, miles):
        meters = miles * 1609.344
        return value.filter(distance__lte=meters).count()


class PaginatedCountSerializer(pagination.PaginationSerializer):
    distance_counts = DistanceCountField(source='paginator.object_list')

    class Meta:
        # Class omitted, just a standard model serializer
        object_serializer_class = MyModelSerializer 

我还给查询集中的每个对象添加了一个距离注释,这样过滤功能才能正常工作。

11

使用 SerializerMethodField,就像在这个 解决方案 中提到的那样。

它可以用来向你对象的序列化表示中添加任何类型的数据。 (REST框架文档)

以下是文档中的一个例子:

from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    days_since_joined = serializers.SerializerMethodField()

    class Meta:
        model = User

    def get_days_since_joined(self, obj):
        return (now() - obj.date_joined).days
51

因为你似乎在使用Rest框架中的一个ListView,你可以在你的类里重写list()方法,然后对返回的数据设置新的值,像这样:

    def list(self, request, *args, **kwargs):
        response = super().list(request, args, kwargs)
        # Add data to response.data Example for your object:
        response.data['10_mi_count'] = 10 # Or wherever you get this values from
        response.data['20_mi_count'] = 30
        response.data['30_mi_count'] = 45
        return response

注意,你的类必须直接继承ListModelMixin,或者通过Rest框架API中的GenericView来继承(http://www.django-rest-framework.org/api-guide/generic-views#listmodelmixin)。我不太确定这是否是正确的方法,但这算是一个快速的解决办法。

希望这对你有帮助!

撰写回答