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
相关产品推荐
相关产品推荐

