You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何筛选余额大于0或小于0的客户列表?

问题描述

我已经创建了一个供React前端调用的API,现有一个CustomerViewSet可以正常展示所有客户列表,retrieve方法也能展示单个客户详情及已购订单,还会计算客户余额。现在需要实现获取所有余额大于0或小于0的客户列表,请问有什么实现思路?

class CustomerViewSet(viewsets.ViewSet):
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def list(self, request):
        customer = Customer.objects.all()
        serializer = CustomerSerializer(customer, many=True, context={"request": request})
        response_dict = {"error": False, "message": "All Customers List Data", "data": serializer.data}
        return Response(response_dict)

    def create(self, request):
        try:
            serializer = CustomerSerializer(data=request.data, context={"request": request})
            serializer.is_valid(raise_exception=True)
            serializer.save()
            dict_response = {"error": False, "message": "Customer Data Stored Successfully"}
        except:
            dict_response = {"error": True, "message": "Phone Number Exists In Database, Fill All Fields"}

        return Response(dict_response)

    def retrieve(self, request, pk=None):
        queryset = Customer.objects.all()
        customer = get_object_or_404(queryset, pk=pk)
        serializer = CustomerSerializer(customer, context={"request": request})

        serializer_data = serializer.data
        # Accessing All the Orders Details of Current Customer
        orders_details = Orders.objects.filter(customer_id=serializer_data["id"]).order_by('-id')
        orders_details_serializers = OrdersSerializer(orders_details, many=True)
        serializer_data["orders"] = orders_details_serializers.data

        # Accessing All Orders of Current Customer
        orders_count = Orders.objects.filter(customer_id=serializer_data["id"])
        orders_count_serializer = OrdersSerializer(orders_count, many=True, context={"request": request})

        # Total orders amount of current customer
        orders_total = Orders.objects.filter(customer_id=serializer_data["id"])
        amount = 0
        discount = 0
        kgs = 0
        for total in orders_total:
            amount = amount + float(total.amount)
            discount = discount + float(total.discount)
            kgs = kgs + float(total.kgs)

        serializer_data1 = serializer.data
        # Accessing All the Payment Details of Current Customer
        payments_details = Payments.objects.filter(customer_id=serializer_data1["id"]).order_by('-id')
        payments_details_serializers = PaymentsSerializer(payments_details, many=True)
        serializer_data["payments"] = payments_details_serializers.data

        serializer_data2 = serializer_data
        payment_count = Payments.objects.filter(customer_id=serializer_data2["id"])
        payment_count_serializer = PaymentsSerializer(payment_count, many=True, context={"request": request})

        # Total Payment of current customer
        payment_total = Payments.objects.filter(customer_id=serializer_data2["id"])
        t_amount = 0
        for balance in payment_total:
            t_amount = t_amount + float(balance.payment)

        balance = amount - t_amount

        dict_response = {"error": False, "message": "Single Data Fetch",
                         "data": serializer_data,
                         "payment": len(payment_count_serializer.data),
                         "buy_total": amount,
                         "payed_total": t_amount,
                         "balance": balance,
                         "kgs": kgs,
                         "discount": discount,
                         "orders_count": len(orders_count_serializer.data)}
        return Response(dict_response)

    def update(self, request, pk=None):
        try:
            queryset = Customer.objects.all()
            customer = get_object_or_404(queryset, pk=pk)
            serializer = CustomerSerializer(customer, data=request.data, context={"request": request})
            serializer.is_valid(raise_exception=True)
            serializer.save()
            dict_response = {"error": False, "message": "Customer Data Updated Successfully"}
        except:
            dict_response = {"error": True, "message": "An Error Occurred"}

        return Response(dict_response)

    def destroy(self, request, pk=None):
        queryset = Customer.objects.all()
        customer = get_object_or_404(queryset, pk=pk)
        # serializer = PaymentsSerializer(customer, context={"request": request})
        customer.delete()
        return Response({"error": False, "message": "Customer Deleted"})
解决方案

思路一:数据库层面计算并过滤(推荐)

直接通过ORM聚合查询计算每个客户的余额,再过滤符合条件的记录,效率远高于内存计算,适合大数据量场景。

实现要求

确保Customer与Orders、Payments模型有正确外键关联,比如:

  • Orders模型:customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='orders')
  • Payments模型:customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='payments')

修改后的list方法

from django.db.models import Sum, F

def list(self, request):
    # 接收前端过滤参数,比如?balance=gt0(余额>0)或balance=lt0(余额<0)
    balance_filter = request.query_params.get('balance', None)
    
    # 聚合计算每个客户的总订单额、总支付额、余额
    customers_queryset = Customer.objects.annotate(
        total_order_amount=Sum('orders__amount', default=0.0),
        total_payment=Sum('payments__payment', default=0.0),
        balance=F('total_order_amount') - F('total_payment')
    )
    
    # 应用过滤条件
    if balance_filter == 'gt0':
        customers_queryset = customers_queryset.filter(balance__gt=0)
    elif balance_filter == 'lt0':
        customers_queryset = customers_queryset.filter(balance__lt=0)
    
    serializer = CustomerSerializer(customers_queryset, many=True, context={"request": request})
    
    # 给序列化数据添加余额字段
    for idx, customer in enumerate(customers_queryset):
        serializer.data[idx]['balance'] = customer.balance
    
    response_dict = {
        "error": False, 
        "message": "Filtered Customers List Data", 
        "data": serializer.data
    }
    return Response(response_dict)

思路二:内存中过滤(仅适合小数据量)

若暂时不想修改数据库查询逻辑,可先获取所有客户,在内存中计算余额后过滤。但数据量大时性能差,不推荐。

修改后的list方法

from django.db.models import Sum

def list(self, request):
    balance_filter = request.query_params.get('balance', None)
    customers = Customer.objects.all()
    serializer = CustomerSerializer(customers, many=True, context={"request": request})
    filtered_data = []
    
    for customer_data in serializer.data:
        # 计算当前客户的总订单额和总支付额
        total_order = Orders.objects.filter(customer_id=customer_data['id']).aggregate(Sum('amount'))['amount__sum'] or 0.0
        total_payment = Payments.objects.filter(customer_id=customer_data['id']).aggregate(Sum('payment'))['payment__sum'] or 0.0
        balance = total_order - total_payment
        
        # 根据条件筛选
        if (balance_filter == 'gt0' and balance > 0) or (balance_filter == 'lt0' and balance < 0) or not balance_filter:
            customer_data['balance'] = balance
            filtered_data.append(customer_data)
    
    response_dict = {
        "error": False, 
        "message": "Filtered Customers List Data", 
        "data": filtered_data
    }
    return Response(response_dict)

额外优化建议

现有retrieve方法多次重复查询Orders和Payments,可合并为聚合查询减少数据库请求:

# 替换retrieve中的循环计算部分
order_agg = Orders.objects.filter(customer_id=customer.id).aggregate(
    total_amount=Sum('amount', default=0.0),
    total_discount=Sum('discount', default=0.0),
    total_kgs=Sum('kgs', default=0.0),
    order_count=Count('id')
)
payment_agg = Payments.objects.filter(customer_id=customer.id).aggregate(
    total_payment=Sum('payment', default=0.0),
    payment_count=Count('id')
)

amount = order_agg['total_amount']
discount = order_agg['total_discount']
kgs = order_agg['total_kgs']
orders_count = order_agg['order_count']
t_amount = payment_agg['total_payment']
payment_count = payment_agg['payment_count']
balance = amount - t_amount

内容的提问来源于stack exchange,提问作者Ahmed Sereta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:10:57