如何在 DRF 中设置序列化器字段的值

问题描述 投票:0回答:1

我有一个网页,其中显示了一些卖家和客户之间交易的报告。因此,为了这个目的,我需要创建一个 API 来从数据库中获取所有交易,提取必要的数据并将它们序列化以在网页中有用。所以我不想创建任何模型,只是以 JSON 格式返回数据。

首先我像这样创建了我的序列化器:

from rest_framework import serializers
from django.db.models import Sum, Count
from account.models import User


class IncomingTradesSerializer(serializers.Serializer):
    all_count = serializers.IntegerField()
    all_earnings = serializers.IntegerField()
    successful_count = serializers.IntegerField()
    successful_earnings = serializers.IntegerField()

    def __init__(self, *args, **kwargs):
        self.trades = kwargs.pop('trades', None)
        super().__init__(*args, **kwargs)

    def get_all_count(self, obj):
        return self.trades.count()

    def get_all_earnings(self, obj):
        return sum(trade.trade_price for trade in self.trades)

    def get_successful_count(self, obj):
        return self.trades.exclude(failure_reason=None).count()

    def get_successful_earnings(self, obj):
        return sum(trade.trade_price for trade in self.trades.exclude(failure_reason=None))


class TradesDistributionSerializer(serializers.Serializer):
    sellers = serializers.DictField()

    def __init__(self, *args, **kwargs):
        self.trades = kwargs.pop('trades', None)
        super().__init__(*args, **kwargs)

    def get_sellers(self, obj):
        sellers = {}

        for user in User.objects.all():
            distributed_trades = self.trades.filter(creator=user)
            sellers[user.username] = sum(
                trade.trade_price for trade in distributed_trades)
            
        return sellers

然后我的

apiView
看起来像这样:

from rest_framework.views import APIView
from rest_framework.response import Response

from trade.models import Trade
from report.serializers import IncomingTradesSerializer, TradesDistributionSerializer

class IndicatorView(APIView):
    def get(self, request):
        trades = Trade.objects.all()

        incoming_trades_serializer = IncomingTradesSerializer(trades=trades)
        trades_distribution_serializer = TradesDistributionSerializer(trades=trades)

        results = {
            'incomingTrades': incoming_trades_serializer.data,
            'tradesDistribution': trades_distribution_serializer.data
        }
        return Response(results)

问题出在

get_fieldname
未调用的方法中,因此最后的响应由 null 或空值组成:

{
  "incomingTrades": {
    "all_count": null,
    "all_earnings": null,
    "successful_count": null,
    "successful_earnings": null
  },
  "tradesDistribution": {
    "sellers": {}
  }
}

我已经将

integerField
DictField
更改为
MethodField
但问题没有解决,唯一的变化是字段响应消失了,唯一的东西是序列化器的两个空字典。

我尝试的另一种方法是重写

to_representation
方法,但和以前一样(当然我的经理告诉我,如果字段数量增加,这种方法性能不好)。

有什么问题吗?
我是否需要改变我的方法或做一些事情,例如覆盖另一种方法或其他方法?
这种情况的标准方法是什么?

python django django-rest-framework django-serializer
1个回答
0
投票

您应该使用

SerializerMethodField
并且坚持使用 Django ORM 来获取所需的数据而不是迭代它:

views.py

class IndicatorView(APIView):
    def get(self, request):
        serializer = IndicatorSerializer(Trade.objects.all())
        return Response(serializer.data)

序列化器.py

class IndicatorSerializer(serializers.Serializer):
    incoming_trades = serializers.SerializerMethodField()
    trades_distribution = serializers.SerializerMethodField()

    def get_incoming_trades(self, trades):
        """
        If there is a reason for failure then .exclude(failure_reason=None)
        would yield UNSUCCESFULL trades.

        Thus, if you want successfull ones, that would be: 
        .filter(failure_reason=None).count()
        """

        incoming_trades = {
            'all_count': trades.count(),
            'all_earnings': trades.aggregate(total=Sum("trade_price"))['total'],
            'successful_count': trades.filter(failure_reason=None).count(),
            'successful_earnings': (
                trades.filter(failure_reason=None)
                .aggregate(total=Sum("trade_price"))['total']),
            'unsuccessful_count': trades.exclude(failure_reason=None).count(),
            'unsuccessful_earnings': (
                trades.exclude(failure_reason=None)
                .aggregate(total=Sum("trade_price"))['total']),
        }
        return incoming_trades

    def get_trades_distribution(self, trades):
        """
        Note that just like your query
        this does not distinguish successful / unsuccessful trades

        Therefore, you should filter the QS if that is your intention.
        """

        trades_distribution =(
            trades.order_by("id")
            .annotate(seller=F("creator__username"))
            .values("seller")
            .annotate(trades_total=Sum("trade_price"))
            .order_by()
        )
        return trades_distribution

回应

{
    "incoming_trades": {
        "all_count": 3,
        "all_earnings": 733.76,
        "successful_count": 2,
        "successful_earnings": 165.87,
        "unsuccessful_count": 1,
        "unsuccessful_earnings": 567.89
    },
    "trades_distribution": [
        {
            "seller": "admin",
            "trades_total": 691.34
        },
        {
            "seller": "someuser",
            "trades_total": 42.42
        }
    ]
}
© www.soinside.com 2019 - 2024. All rights reserved.