Django Rest Framework,group_by 使用 .values() 查询,并嵌入相关记录

Django Rest Framework, group_by queries using .values(), and embedding a related record

在 Django 查询设置为 return 按聚合值分组时使用 .values() 时,我不知道如何将相关记录嵌入到 Django Rest Framework 响应中。下面包含示例模型、序列化程序和视图集以及当前响应和所需响应示例。

每笔交易都有一种证券。一个证券可以存在多个交易。我需要单独显示交易并按安全性分组。按证券分组时,我想 return 响应中的嵌入式证券以及相关交易的聚合值,但到目前为止我只能获得证券 ID returned。任何在响应中获取安全性本身的尝试都会导致异常 'int' object has no attribute 'pk'

贸易模式

class Trade(models.Model):
    security = models.ForeignKey('Security')
    ticker = models.CharField(
        max_length=128,
        null=True,
        blank=True,
        db_index=True,
    )
    shares = models.FloatField(db_index=True)
    value_usd = models.FloatField(db_index=True)
    days_to_trade = models.FloatField(db_index=True)
    trade_date = models.DateField(
        db_index=True,
        null=True,
        blank=True,
    )

安全模型

class Security(models.Model):
    name = models.CharField(
        db_index=True,
        blank=True,
        null=True,
    )
    ipo_date = models.DateField(
        null=True,
        blank=True
    )

贸易观点

class TradeViewSet(viewsets.ModelViewSet):
    def get_queryset(self):
        // param and filter setup/defaults snipped for clarity
        ...
        qs = Trade.objects.filter(**qs_filter)
        // if group_by param is passed in, then group the trades by ticker/security and return aggregate sums for value, shares, and days to trade
        if group_by:
            qs = qs.values('ticker', 'security_id').annotate(value_usd=Sum('value_usd'), shares=Sum('shares'), days_to_trade=Sum('days_to_trade'), num_trades=Count('security')).order_by('ticker')
            self.serializer_class = TradeByTickerSerializer
        return qs

    serializer_class = TradeSerializer

贸易序列化程序

class TradeSerializer(serializers.ModelSerializer):
    security = SecuritySerializer(many=False, read_only=True)
    class Meta:
        model = Trade
        fields = [
            'id',
            'security',
            'trade_date',
            'ticker',
            'shares',
            'value_usd',
            'days_to_trade',
        ]
        depth = 1

TradeByTicker 序列化器

class TradeByTickerSerializer(serializers.ModelSerializer):
    num_trades = serializers.IntegerField(read_only=True)
    // security = SecuritySerializer(many=False, read_only=True) // this gives an exception 
    class Meta:
        model = Trade
        fields = [
            // 'security', // I tried this but get an exception
            'security_id',
            'ticker',
            'shares',
            'value_usd',
            'days_to_trade',
            'num_trades',
        ]

当前输出

{
    "results": [
        {
            "securityId": 123,
            "ticker": "ABC-US",
            "shares": 790611.154356048,
            "valueUsd": 15598758.0754448,
            "daysToTrade": 7.2460672754406,
            "numTrades": 1
        },
        {
            "securityId": 456,
            "ticker": "DEF-US",
            "shares": 1116548.93406872,
            "valueUsd": 29041437.7751273,
            "daysToTrade": 6.6811609336384,
            "numTrades": 3
        },
        {
            "securityId": 789,
            "ticker": "HIJ-US",
            "shares": 979099.946772097,
            "valueUsd": 16360760.1105617,
            "daysToTrade": 6.50616625094424,
            "numTrades": 2
        },
        ...
]

期望的输出

{
    "results": [
        {
            "security": {
                "id": 123,
                "name": "Another Boring Company",
                "ipoDate": "1988-12-23"
            },
            "ticker": "ABC",
            "shares": 790611.154356048,
            "valueUsd": 15598758.0754448,
            "daysToTrade": 7.2460672754406,
            "numTrades": 1
        },
        {
            "security": {
                "id": 456,
                "name": "Def Jam Records",
                "ipoDate": "2001-04-24"
            },
            "ticker": "DEF",
            "shares": 1116548.93406872,
            "valueUsd": 29041437.7751273,
            "daysToTrade": 6.6811609336384,
            "numTrades": 3
        },
        {
            "security": {
                "id": 789,
                "name": "Hijinx Corp",
                "ipoDate": "1999-12-31"
            },
            "ticker": "HIJ",
            "shares": 979099.946772097,
            "valueUsd": 16360760.1105617,
            "daysToTrade": 6.50616625094424,
            "numTrades": 2
        },
        ...
]

您可以使用 SerializerMethodField 来处理获取相关的安全详细信息,如下所示:

class TradeByTickerSerializer(serializers.ModelSerializer):
    num_trades = serializers.IntegerField(read_only=True)
    security = serializer.SerializerMethodField()

    class Meta:
        model = Trade
        fields = [
            'security',
            'security_id',
            'ticker',
            'shares',
            'value_usd',
            'days_to_trade',
            'num_trades',
        ]

    def get_security(self, obj)
        return SecuritySerializer(Security.objects.get(pk=obj['security_id'])).data

或者由于查询集不再包含 Trade 个对象,我建议只使用 Serializer(非 ModelSerializer):

class TradeByTickerSerializer(serializers.Serializer):
    security = serializers.SerializerMethodField()
    ticker = serializers.FloatField(read_only=True)
    shares = serializers.FloatField(read_only=True)
    value_usd = serializers.FloatField(read_only=True)
    days_to_trade = serializers.FloatField(read_only=True)
    num_trades = serializers.IntegerField(read_only=True)

    def get_security(self, obj)
        return SecuritySerializer(Security.objects.get(pk=obj['security_id'])).data

请注意,这将根据查询集的组结果访问数据库,只是为了获取相关的 Security 对象。