尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
零基础入门python37:订单和订单项——为什么要保存价格快照
零基础入门python37订单和订单项——为什么要保存价格快照一、上一篇课后练习讲解购物车数量接口应读取当前数量后加减并限制结果在 1 到库存之间不要让客户端直接提交一个超过库存的最终值而跳过校验。上一篇课后练习完整答案上一篇练习的要求已落实到下面完整文件先运行项目测试再用 curl 对照状态码和数据库持久化结果答案要点购物车只在 Session 保存 id/数量商品价格从数据库读取数量 1—99 校验并清理下架商品。文件shop/cart.py完整参考答案文件完整文件shop/cart.pyfromdjango.httpimportJsonResponsefrom.modelsimportProductdefadd(request,product_id):productProduct.objects.get(pkproduct_id,is_activeTrue)quantityint(request.POST.get(quantity,1))ifnot1quantity99:returnJsonResponse({error:quantity_invalid},status400)cartrequest.session.get(cart,{})cart[str(product.id)]cart.get(str(product.id),0)quantity request.session[cart]cart;request.session.modifiedTruereturnJsonResponse({cart:cart})完整参考答案文件本篇对应的交付源码完整文件django-shop/shop/views.pyfromrest_frameworkimportgenerics,permissions,status,viewsetsfromrest_framework.responseimportResponsefrom.modelsimportCategory,Order,Productfrom.serializersimportCategorySerializer,CheckoutSerializer,OrderSerializer,ProductSerializerfrom.servicesimportcreate_orderclassCategoryViewSet(viewsets.ReadOnlyModelViewSet):querysetCategory.objects.order_by(name)serializer_classCategorySerializerclassProductViewSet(viewsets.ReadOnlyModelViewSet):serializer_classProductSerializerdefget_queryset(self):queryProduct.objects.filter(is_activeTrue).select_related(category)ifcategory:self.request.query_params.get(category):queryquery.filter(category_idcategory)ifkeyword:self.request.query_params.get(q):queryquery.filter(name__icontainskeyword)returnquery.order_by(-created_at)classCheckoutView(generics.GenericAPIView):permission_classes(permissions.IsAuthenticated,)serializer_classCheckoutSerializerdefpost(self,request):serializerself.get_serializer(datarequest.data)serializer.is_valid(raise_exceptionTrue)ordercreate_order(request.user,serializer.validated_data[items])returnResponse(OrderSerializer(order).data,status201)classMyOrderViewSet(viewsets.ReadOnlyModelViewSet):permission_classes(permissions.IsAuthenticated,)serializer_classOrderSerializerdefget_queryset(self):returnOrder.objects.filter(userself.request.user).prefetch_related(items).order_by(-id)classCartView(generics.GenericAPIView):defget(self,request):returnResponse(request.session.get(cart,{}))defpost(self,request):productgenerics.get_object_or_404(Product,idrequest.data.get(product_id),is_activeTrue)quantityint(request.data.get(quantity,1))ifquantity1orquantityproduct.stock:returnResponse({message:数量必须大于0且不能超过库存},statusstatus.HTTP_400_BAD_REQUEST)cartrequest.session.setdefault(cart,{})cart[str(product.id)]quantity request.session.modifiedTruereturnResponse(cart,statusstatus.HTTP_201_CREATED)defdelete(self,request):cartrequest.session.get(cart,{})cart.pop(str(request.data.get(product_id)),None)request.session.modifiedTruereturnResponse(cart)验收命令python -m pytest -qDjango 项目使用 python manage.py test。预期测试通过若失败先检查迁移、配置和事务回滚。二、本篇完成什么建立 Order 和 OrderItem。Order 保存用户、状态和总额OrderItem 保存下单时的商品名、价格和数量。三、为什么保存快照商品可能改名、调价或下架但历史订单必须显示当时购买的名称和金额。因此 OrderItem 不能只存product_id还要保存product_name和price。classOrderItem(models.Model):ordermodels.ForeignKey(Order,on_deletemodels.CASCADE,related_nameitems)productmodels.ForeignKey(Product,on_deletemodels.PROTECT)product_namemodels.CharField(max_length120)pricemodels.DecimalField(max_digits10,decimal_places2)quantitymodels.PositiveIntegerField()四、验收迁移后创建订单和订单项修改商品价格再读取订单确认订单项价格没有变化。课后练习给 Order 增加 cancelled 状态并禁止已支付订单直接删除。项目增量订单和订单项快照订单项必须保存下单时的商品名和价格快照。商品后来改名或改价历史订单仍要能按原样展示。OrderItem.objects.create(orderorder,productproduct,product_nameproduct.name,unit_priceproduct.price,quantityquantity)金额由服务层计算并在数据库保存详情页不能实时读取商品当前价格。课后练习验证商品改价后旧订单总额不变。五、上一篇练习讲解清理购物车再下单下单服务第一步应读取 Session 并重新查询商品清理下架商品只是用户体验不能替代结算时的库存校验。价格快照写入OrderItem订单总额由服务层计算客户端传来的total一律忽略。六、订单和订单项的领域边界classOrder(models.Model):usermodels.ForeignKey(settings.AUTH_USER_MODEL,on_deletemodels.PROTECT)statusmodels.CharField(max_length20,choicesSTATUS,defaultpending)totalmodels.DecimalField(max_digits12,decimal_places2)created_atmodels.DateTimeField(auto_now_addTrue)classOrderItem(models.Model):ordermodels.ForeignKey(Order,related_nameitems,on_deletemodels.CASCADE)productmodels.ForeignKey(Product,on_deletemodels.PROTECT)product_namemodels.CharField(max_length120)unit_pricemodels.DecimalField(max_digits10,decimal_places2)quantitymodels.PositiveIntegerField()product外键保留关联product_name/unit_price保存快照商品删除被保护订单详情即使商品下架也能展示。订单状态应由有限集合驱动禁止客户端随意传入paid。七、金额计算为什么放在服务层defbuild_order(user,cart):orderOrder(useruser,totalDecimal(0))order.save()totalDecimal(0)forproduct_id,quantityincart.items():productProduct.objects.get(idproduct_id,is_activeTrue)quantityint(quantity)ifquantity0orquantity99:raiseValidationError(quantity_invalid)OrderItem.objects.create(orderorder,productproduct,product_nameproduct.name,unit_priceproduct.price,quantityquantity)totalproduct.price*quantity order.totaltotal order.save(update_fields[total])returnorder真实项目应把上面函数包在transaction.atomic()中本篇先关注快照含义。若循环中第二个商品失败事务会回滚第一个订单项不能留下半张订单。八、验收与故障排查创建订单后执行product.price Decimal(99.00); product.save()再读取订单项unit_price仍应是下单时的值。若总额变成新价格说明详情序列化器误读了item.product.price若出现TypeError: Decimal * float检查数量是否先转为 int、金额是否始终为 Decimal。九、本篇练习增加cancelled状态和取消接口只有 pending 订单可取消已支付订单返回 409测试商品改价后旧订单总额不变。下一篇会把下单过程放进事务并用库存锁处理并发。十、订单金额的舍入规则单价和小计都保留两位小数数量乘法不会产生额外舍入如果未来支持折扣先明确“每行舍入还是总额最后舍入”。推荐在服务层使用Decimal.quantize(Decimal(0.01), ROUND_HALF_UP)并在测试中覆盖19.995这类边界。数据库DecimalField只负责存储精度不能替代业务规则。订单详情序列化时统一转字符串避免 JavaScript 浮点再次损失分。本篇结束完整模块文件下面是交付项目中真实存在的完整文件 django-shop/shop/models.py。它覆盖本篇新增逻辑以及前文已经完成的依赖代码复制单个函数会丢失上下文因此这里提供整份文件。fromdjango.confimportsettingsfromdjango.dbimportmodelsclassCategory(models.Model):namemodels.CharField(max_length60,uniqueTrue)slugmodels.SlugField(max_length60,uniqueTrue)def__str__(self):returnself.nameclassProduct(models.Model):categorymodels.ForeignKey(Category,on_deletemodels.PROTECT,related_nameproducts)namemodels.CharField(max_length120)slugmodels.SlugField(max_length120,uniqueTrue)pricemodels.DecimalField(max_digits10,decimal_places2)stockmodels.PositiveIntegerField(default0)is_activemodels.BooleanField(defaultTrue)created_atmodels.DateTimeField(auto_now_addTrue)def__str__(self):returnself.nameclassOrder(models.Model):classStatus(models.TextChoices):PENDINGpending,待处理PAIDpaid,已支付CANCELLEDcancelled,已取消usermodels.ForeignKey(settings.AUTH_USER_MODEL,on_deletemodels.PROTECT,related_nameorders)statusmodels.CharField(max_length20,choicesStatus.choices,defaultStatus.PENDING)total_amountmodels.DecimalField(max_digits12,decimal_places2,default0)created_atmodels.DateTimeField(auto_now_addTrue)classOrderItem(models.Model):ordermodels.ForeignKey(Order,on_deletemodels.CASCADE,related_nameitems)productmodels.ForeignKey(Product,on_deletemodels.PROTECT)product_namemodels.CharField(max_length120)pricemodels.DecimalField(max_digits10,decimal_places2)quantitymodels.PositiveIntegerField()
RELATED

相关推荐

软件核心资产化安全加固软件的技术路径与实践思考北京市圣天地

软件核心资产化安全加固软件的技术路径与实践思考北京市圣天地

在数字化业务全面渗透的当下,软件开发商、AI技术服务商及企业应用运营方普遍面临核心资产安全威胁:自研代码可能被逆向工程拆解,正版安装包存在非法盗版分发的风险,大模型权重参数可能遭受恶意篡改,高价值数据文件亦可…

📅 2026/10/6 8:18:05
彻底搞懂 IT 中的 Schema(模式/架构):全场景通解(数据库、JSON、后端、AI、RAG)

彻底搞懂 IT 中的 Schema(模式/架构):全场景通解(数据库、JSON、后端、AI、RAG)

一、Schema 核心定义(一句话吃透)Schema 中文:模式 / 架构 / 数据模板本质:一套「数据结构规范」提前定义:数据有哪些字段、字段类型、是否必填、取值范围、层级结构、约束规则。通俗终极比喻:Schema 空白…

📅 2026/10/7 5:22:19
宁夏职业院校学工信息管理系统怎么选?本地选型实用参考

宁夏职业院校学工信息管理系统怎么选?本地选型实用参考

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

📅 2026/10/26 13:20:24
MORE NEWS

更多资讯

📰

廊坊本地考电工证多久拿证 实操考试心里没底怕挂科

廊坊本地考电工证多久拿证 实操考试心里没底怕挂科 实操考试心里没底怕挂科,这是大多数第一次报考电工证的学员最真实的感受。尤其是像你这样的南阳卧龙区学员,可能对考试流程、题型、实操操作都不熟悉,担心一不小心就挂科。但别担心,只要掌握好备考方法…

📰

你之前考的证在外省能不能转过来?无锡市考高压电工证真实经历揭秘

你之前考的证在外省能不能转过来?无锡市考高压电工证真实经历揭秘 之前考的证在外省能不能转过来?这几乎是每个准备考高压电工证的工友最先问的问题。我有个同事小李,就是从河南来的,他在郑州考的高压电工证,后来到无锡打工,发现证书在本地不能直接使用…

📰

汝州考电工证在哪里 一个真实经历告诉你别走弯路

汝州考电工证在哪里 一个真实经历告诉你别走弯路 你是不是也和我一样, 不知道去哪报名怕被中介坑 ?刚听说要在汝州考电工证,结果一查信息就头晕,网上一堆培训机构,价格参差不齐,还有的连资质都没有,真怕一不小心就被坑了。我就有个朋友,当初为了省…

📰

廊坊本地考电工证千万别踩坑,工地太忙根本没时间复习考试

廊坊本地考电工证千万别踩坑,工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,这是很多一线电工的真实写照。很多人在一线干了多年,但因为没有电工证,不仅影响职业发展,还可能面临被处罚的风险。更让人头疼的是, 廊坊本地考电工证…

📰

永川职教中心电工证通过率揭秘:考不过白交钱?真相在这

永川职教中心电工证通过率揭秘:考不过白交钱?真相在这 怕考不过白交培训费?永川职教中心电工证报考前你必须知道的真相, 通过率揭秘 就在这里!别再被培训机构忽悠,咱们今天就来聊聊怎么稳稳拿下电工证,不走弯路,不花冤枉钱!…

📰

弱电电工证操作电压千万别踩坑:新乡水利工程从业者必看备考攻略

弱电电工证操作电压千万别踩坑:新乡水利工程从业者必看备考攻略 你是不是正为找工作发愁?手里缺个特种作业证,企业一问“有证吗”,你就只能摇头?弱电电工证操作电压这事儿,不是你想考就能考,也不是你想过就能过的,稍有不慎,钱花了、时间耗了,证还拿…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬