简体   繁体   English

将额外数据添加到整个结果集的 Django Rest Framework 结果

[英]Adding extra data to Django Rest Framework results for entire result set

I'm using Django Rest Framework and need to add extra data to a result set.我正在使用 Django Rest Framework,需要向结果集添加额外的数据。 Specifically, where you would usually have:具体来说,您通常会拥有:

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

I would like to add extra counts like so:我想添加额外的计数,如下所示:

{
    "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": [
        {...}
    ]
}

The extra counts in this example are just how many of the objects have a field distance with a value less than the miles described in the key.此示例中的额外计数只是具有字段距离值小于键中描述的英里数的对象数量。

My issue is that I have no idea where the best place to extend and insert this behaviour is.我的问题是我不知道扩展和插入此行为的最佳位置在哪里。

Ideally I'd like this to work whether or not the results are paginated, making no assumptions.理想情况下,无论结果是否分页,我都希望它能够工作,不做任何假设。

What I'm really after here is a nod in the right direction (and why that is the right place for it go).我真正追求的是朝着正确的方向点头(以及为什么这是正确的地方)。

I've checked the docs and can't find anything that describes how to add things like this, but I would be more than happy to be proven wrong on that score.我已经检查了文档,但找不到任何描述如何添加此类内容的内容,但我很乐意在该分数上被证明是错误的。

Since you seem to be using one of the ListViews from the Rest Framework, you could override the list() method in your class and set new values on the resulting data, like this:由于您似乎正在使用 Rest Framework 中的 ListViews 之一,您可以覆盖类中的 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

Notice that your class must inherit the ListModelMixin directly or via a GenericView from the Rest Framework API ( http://www.django-rest-framework.org/api-guide/generic-views#listmodelmixin ).请注意,您的类必须直接或通过 Rest Framework API ( http://www.django-rest-framework.org/api-guide/generic-views#listmodelmixin ) 中的 GenericView 继承 ListModelMixin。 I really don't know if it is the right way to do this, but it is a quick fix.我真的不知道这样做是否正确,但这是一个快速解决方案。

Hope it helps!希望能帮助到你!

Use SerializerMethodField as mentioned in this solution .使用本解决方案中提到的SerializerMethodField

It can be used to add any sort of data to the serialized representation of your object.它可用于将任何类型的数据添加到对象的序列化表示中。 ( REST framework doc ) REST 框架文档

Example from the documentation:文档中的示例:

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

In the end I just created a custom pagination serializer with a field like so:最后,我刚刚创建了一个自定义分页序列化程序,其字段如下:

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 

I also added a distance annotation to each object in the queryset for the filtering to work on.我还为查询集中的每个对象添加了一个距离注释,以便进行过滤。

I used DRF with pagination as well and wanted to add a summary of the returned queryset itself, so first I had to get the queryset in the list method.我也将 DRF 与分页一起使用,并希望添加返回的查询集本身的摘要,因此首先我必须在列表方法中获取查询集。

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)

This gave me the following response:这给了我以下答复:

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

Note: this will work for non-paginated results as well.注意:这也适用于非分页结果。

I'm piggybacking off the accepted answer here!我在这里捎带接受的答案!

I think the main issue is that the serializer returns a ReturnList, which is just a regular list with some extra context.我认为主要问题是序列化程序返回一个 ReturnList,它只是一个带有一些额外上下文的常规列表。

You can just replace the data with your own OrderedDict like so:您可以像这样用自己的 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 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM