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

Django订单保存后删购物车,订单项丢失问题及方案咨询

解决方案:让订单永久保存购物车商品信息

问题根源确实是service_items = models.ManyToManyField(Cart)——多对多关联的是临时的购物车实例,当你删除购物车后,关联的中间表记录会被自动清理,导致订单的service_items变成空数组。

订单应该是购物车的快照,需要永久保存下单时的商品状态,而不是依赖临时的购物车对象。最合理的实现方式是新增一个OrderItem模型,专门存储订单的商品条目,具体修改如下:

1. 修改模型(models.py)

新增OrderItem模型,复制购物车的核心字段,关联到订单而非用户:

class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='service_items')
    service_item = models.ForeignKey(ServiceItem, on_delete=models.PROTECT)
    is_wash = models.BooleanField(default=False)
    is_stain_treatment = models.BooleanField(default=False)
    is_iron = models.BooleanField(default=False)
    is_dry_wash = models.BooleanField(default=False)
    quantity = models.SmallIntegerField()
    unit_price = models.DecimalField(max_digits=6, decimal_places=2)
    price = models.DecimalField(max_digits=6, decimal_places=2)

    def __str__(self):
        return f'{self.order} - {self.service_item}'

# 修改Order模型,删除原来的多对多关联
class Order(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    # 移除这行:service_items = models.ManyToManyField(Cart)
    status = models.BooleanField(db_index=True, default=False)
    total = models.DecimalField(max_digits=6, decimal_places=2)
    order_date = models.DateTimeField(db_index=True, auto_now_add=True)
    delivery_date = models.DateTimeField(db_index=True, default=datetime.now() + timedelta(days = 1))
    advance_payment = models.DecimalField(max_digits=6, decimal_places=2, default=0)
    payment_method = models.CharField(max_length=70, db_index=True, default="cash")
    
    def __str__(self):
        return f'{self.customer} - {self.order_date}'

2. 修改序列化器(serializers.py)

新增OrderItemSerializer,并更新OrderSerializer的关联字段:

class OrderItemSerializer(serializers.ModelSerializer):
    service_item = ServiceItemSerializer(read_only=True)
    class Meta:
        model = OrderItem
        fields = '__all__'

class OrderSerializer(serializers.ModelSerializer):
    due_payment = serializers.SerializerMethodField(method_name='calculate_balance')
    service_items = OrderItemSerializer(many=True, read_only=True)
    class Meta:
        model = Order
        fields = ['id', 'customer', 'status', 'total', 'order_date', 'delivery_date',
                    'advance_payment', 'payment_method', 'due_payment', 'service_items']
        depth = 1

    def calculate_balance(self, product:Order):
        if product.advance_payment > 0:
            return Decimal(product.total - product.advance_payment)
        else:
            return Decimal(product.total)

3. 修改订单创建逻辑(views.py)

创建订单时,遍历购物车条目,复制数据到OrderItem,再删除购物车:

@api_view(['GET', 'POST'])
@throttle_classes([UserRateThrottle])
def orders(request):
    if request.method == 'GET':
        orders = Order.objects.all()
        serialized_orders = OrderSerializer(orders, many=True)
        return Response(serialized_orders.data, status=status.HTTP_200_OK)

    elif request.method == 'POST':
        customer_phone = request.data.get("customer_phone")
        customer = get_object_or_404(Customer, phone_number=customer_phone)
        cart = Cart.objects.filter(customer=customer)
        if cart:
            total = cart.aggregate(Sum('price'))['price__sum']
            delivery_date = request.data.get("delivery_date")
            advance_payment = Decimal(request.data.get("advance_payment", 0))
            payment_method = request.data.get("payment_method", "cash")
            
            order = Order.objects.create(
                customer=customer, 
                total=total, 
                delivery_date=delivery_date, 
                advance_payment=advance_payment, 
                payment_method=payment_method
            )
            
            # 复制购物车数据到OrderItem
            for cart_item in cart:
                OrderItem.objects.create(
                    order=order,
                    service_item=cart_item.service_item,
                    is_wash=cart_item.is_wash,
                    is_stain_treatment=cart_item.is_stain_treatment,
                    is_iron=cart_item.is_iron,
                    is_dry_wash=cart_item.is_dry_wash,
                    quantity=cart_item.quantity,
                    unit_price=cart_item.unit_price,
                    price=cart_item.price
                )
            
            cart.delete()
            # 返回完整的订单数据(包含service_items)
            serializer = OrderSerializer(order)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response({'message': 'Cart is Empty'}, status=status.HTTP_204_NO_CONTENT)

为什么这样做?

  • 订单是不可变的快照:下单后商品的价格、选项(如是否干洗)不应该随购物车或商品信息的变化而改变,用OrderItem存储独立副本能保证数据一致性。
  • 购物车是临时存储:用户下单后清空购物车是合理行为,此时订单的商品信息不应该依赖购物车的存在。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:20:41