【问题标题】:DRF : How to sum value from nested object and order by sum valueDRF:如何从嵌套对象中求和值并按总和值排序
【发布时间】:2022-06-14 09:24:37
【问题描述】:

我已经创建了使用序列化器作为文档的序列化嵌套

models.py

class Category(models.Model):
    name = models.CharField("Name", "name", max_length=255)
    iconname = models.CharField("Icon Name", "iconname", max_length=255)
    budgetamount = models.DecimalField(
        max_digits=19, decimal_places=2, default=0)
    iconcolor = models.CharField(
        "Icon Color", "iconcolor", default='4294951175', max_length=255)

    def __str__(self):
        return self.name


class DailyExpense(models.Model):
    payee_item_desc = models.CharField(
        "Payee Item Description", "payee_item_desc", max_length=255)
    category = models.ForeignKey(
        Category, related_name='dailyexpense_category', on_delete=models.CASCADE, blank=True, null=True)
    amount = models.DecimalField(max_digits=19, decimal_places=2)
    remarks = models.CharField(
        "Remarks", "remarks", max_length=255, blank=True, null=True)
    tran_date = models.DateTimeField()
    isnotclear = models.BooleanField(default=False)

    def __str__(self):
        return self.payee_item_desc

serializers.py

class DailyExpenseSerializer(serializers.ModelSerializer):
    class Meta:
        model = DailyExpense
        fields = "__all__"


class CategoryWithDailyExpenseSerializer(serializers.ModelSerializer):
    dailyexpense_category = DailyExpenseSerializer(
        source='filtered_dailyexpense_category', many=True, read_only=True)

    class Meta:
        model = Category
        fields = ('id', 'name', 'iconname',
                  'budgetamount', 'iconcolor', 'dailyexpense_category')

views.py

class CategoryWithDailyExpenseViewSet(viewsets.ModelViewSet):
    def get_queryset(self):
        fromDate = parse_datetime(self.request.query_params.get(
            'fromDate') + ' ' + '00:00:00').strftime('%Y-%m-%d %H:%M:%S')
        toDate = parse_datetime(self.request.query_params.get(
            'toDate') + ' ' + '00:00:00').strftime('%Y-%m-%d %H:%M:%S')
        queryset = Category.objects.prefetch_related(
            Prefetch('dailyexpense_category', queryset=DailyExpense.objects.filter(
                tran_date__range=[fromDate, toDate]).order_by('tran_date'), to_attr='filtered_dailyexpense_category')
        )
        return queryset

    # queryset = Category.objects.all().order_by('name')
    serializer_class = CategoryWithDailyExpenseSerializer
    filter_class = CategoryFilter

我得到的结果如下

[
     {
        "id": 2,
        "name": "Foods:Breakfast",
        "iconname": "emoji_food_beverage",
        "budgetamount": "0.00",
        "iconcolor": "4294951175",
        "dailyexpense_category": [
            {
                "id": 24574,
                "payee_item_desc": "เซเว่น",
                "amount": "-100.00",
                "remarks": "เฟิส",
                "tran_date": "2022-04-01T00:00:00Z",
                "isnotclear": false,
                "category": 2
            },
            {
                "id": 19933,
                "payee_item_desc": "เซเว่น",
                "amount": "-100.00",
                "remarks": "เฟิส",
                "tran_date": "2022-04-03T00:00:00Z",
                "isnotclear": false,
                "category": 2
            }
        ]
    },
     {
        "id": 31,
        "name": "Sport",
        "iconname": "sports_basketball_outlined",
        "budgetamount": "0.00",
        "iconcolor": "4294951175",
        "dailyexpense_category": [
            {
                "id": 25636,
                "payee_item_desc": "น้ำเกลือแร่",
                "amount": "-20.00",
                "remarks": "",
                "tran_date": "2022-04-01T00:00:00Z",
                "isnotclear": false,
                "category": 31
            },
            {
                "id": 26682,
                "payee_item_desc": "ดีแคทล่อน",
                "amount": "-6700.00",
                "remarks": "",
                "tran_date": "2022-04-01T00:00:00Z",
                "isnotclear": false,
                "category": 31
            },
            {
                "id": 28592,
                "payee_item_desc": "น้ำเกลือแร่",
                "amount": "-20.00",
                "remarks": "",
                "tran_date": "2022-04-02T00:00:00Z",
                "isnotclear": false,
                "category": 31
            }
        ]
    },
]

我想对嵌套对象中的金额值求和,以在每个父对象中显示如下,并按总金额排序

[
     {
        "id": 31,
        "name": "Sport",
        "iconname": "sports_basketball_outlined",
        "budgetamount": "0.00",
        "iconcolor": "4294951175",
        "sum_amount": "-6740.00",
        "dailyexpense_category": [
            {
                "id": 25636,
                "payee_item_desc": "น้ำเกลือแร่",
                "amount": "-20.00",
                "remarks": "",
                "tran_date": "2022-04-01T00:00:00Z",
                "isnotclear": false,
                "category": 31
            },
            {
                "id": 26682,
                "payee_item_desc": "ดีแคทล่อน",
                "amount": "-6700.00",
                "remarks": "",
                "tran_date": "2022-04-01T00:00:00Z",
                "isnotclear": false,
                "category": 31
            },
            {
                "id": 28592,
                "payee_item_desc": "น้ำเกลือแร่",
                "amount": "-20.00",
                "remarks": "",
                "tran_date": "2022-04-02T00:00:00Z",
                "isnotclear": false,
                "category": 31
            }
        ]
    },
    {
        "id": 2,
        "name": "Foods:Breakfast",
        "iconname": "emoji_food_beverage",
        "budgetamount": "0.00",
        "iconcolor": "4294951175",
        "sum_amount": "-200.00",
        "dailyexpense_category": [
            {
                "id": 24574,
                "payee_item_desc": "เซเว่น",
                "amount": "-100.00",
                "remarks": "เฟิส",
                "tran_date": "2022-04-01T00:00:00Z",
                "isnotclear": false,
                "category": 2
            },
            {
                "id": 19933,
                "payee_item_desc": "เซเว่น",
                "amount": "-100.00",
                "remarks": "เฟิส",
                "tran_date": "2022-04-03T00:00:00Z",
                "isnotclear": false,
                "category": 2
            }
        ]
    }
]

是否可以在 django rest 框架中执行此操作,或者我必须用前端语言对其进行总结?

【问题讨论】:

    标签: django django-models django-rest-framework django-views


    【解决方案1】:

    您可以在序列化程序类中使用SerializerMethodField

    class CategoryWithDailyExpenseSerializer(serializers.ModelSerializer):
        dailyexpense_category = DailyExpenseSerializer(
            source='filtered_dailyexpense_category', many=True, read_only=True)
        sum_amount = serializers.SerializerMethodField()
    
        class Meta:
            model = Category
            fields = ('id', 'name', 'iconname', 'sum_amount',
                      'budgetamount', 'iconcolor', 'dailyexpense_category')
    
        def get_sum_amount(self, obj):
            fromDate = parse_datetime(self.context['request'].query_params.get(
                'fromDate') + ' ' + '00:00:00').strftime('%Y-%m-%d %H:%M:%S')
            toDate = parse_datetime(self.context['request'].query_params.get(
                'toDate') + ' ' + '00:00:00').strftime('%Y-%m-%d %H:%M:%S')
            return obj.dailyexpense_category.aggregate(Sum('amount', filter = Q(dailyexpense_category__tran_date__range=[fromDate, toDate])))['amount__sum']
    

    或者您可以将此聚合函数添加到序列化程序的查询集中。

    class CategoryWithDailyExpenseViewSet(viewsets.ModelViewSet):
        def get_queryset(self):
            ...
            queryset = Category.objects.prefetch_related(
                Prefetch('dailyexpense_category', queryset=DailyExpense.objects.filter(
                    tran_date__range=[fromDate, toDate]).order_by('tran_date'), to_attr='filtered_dailyexpense_category')
            ).aggregate(Sum('amount', filter = Q(dailyexpense_category__tran_date__range=[fromDate, toDate]))).orderBy('-amount')
            return queryset
    

    【讨论】:

    • 按照您的建议添加,总和是嵌套对象中所有数据的总和,而不是预取对象的总和,先生
    • 好的,我了解您当前的 2 个帖子。我会改的
    • 我在Sum函数中添加了条件。所以请再次检查。
    • 好的,我还添加了 fromDatetoDate 值。所以它应该工作。请再次检查。
    • 唯一的问题是这个聚合函数将为每个 Category 实例调用,这将向数据库添加 N 个额外的命中。您可以在单个数据库命中中从主查询中获取聚合。
    猜你喜欢
    • 2012-11-25
    • 2019-07-02
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 2020-10-03
    • 2021-03-27
    • 2021-08-09
    • 1970-01-01
    相关资源
    最近更新 更多