Как установить значение для поля сериализаторов в DRF

У меня есть веб-страница, на которой я показываю некоторые отчеты о сделках между продавцами и покупателями. Итак, для этой цели мне нужно создать 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, которые не вызываются, поэтому ответ в конце состоит из нулевых или пустых значений:

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

Я уже изменил integerFields и DictField на MethodField, но проблема не была решена, и единственным изменением было то, что поля исчезли в ответ, и единственными вещами были два пустых словаря для сериализаторов.

Я попробовал еще один способ — переопределить метод to_representation, но он был таким же, как и раньше (конечно, мой менеджер сказал мне, что этот метод снижает производительность, если количество полей увеличивается).

В чем проблема?
Нужно ли мне изменить свой подход или сделать что-то вроде переопределения другого метода или чего-то еще?
Каков стандартный способ для этого сценария?

get_FIELD_NAME(...) будет вызван, если вы определили поле как serializers.SerializerMethodField() (вместо IntegerField9...)\

JPG 27.08.2024 16:32

Как я уже упоминал выше, я попробовал этот способ, но в ответ было два пустых словаря @JPG.

Mahdi 27.08.2024 16:34
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
2
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам следует использовать SerializerMethodField, а также придерживаться Django ORM для извлечения данных вместо их итерации:

view.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
        }
    ]
}

P.S. Ознакомьтесь с aggregation документацией о том, как group_by, что может оказаться немного сложнее.

Это отличный подход, спасибо за ответ, все сработало нормально...

Mahdi 28.08.2024 21:30

Другие вопросы по теме