DRF 商城项目 - 购物( 购物车, 订单, 支付 )逻辑梳理
购物车
购物车模型
购物车中的数据不应该重复. 即对相同商品的增加应该是对购买数量的处理而不是增加一条记录
因此对此进行联合唯一索引, 但是也因此存在一些问题
class ShoppingCart(models.Model):
user = models.ForeignKey(User, verbose_name=u"用户")
goods = models.ForeignKey(Goods, verbose_name=u"商品")
nums = models.IntegerField(default=0, verbose_name="购买数量") add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间") class Meta:
verbose_name = '购物车'
verbose_name_plural = verbose_name
unique_together = ("user", "goods") # 一个商品不应该在购物车中重复 def __str__(self):
return "%s(%d)".format(self.goods.name, self.nums)
购物车序列化组件
选择序列化方式
数据库中设定联合唯一索引之后. 如果对某一商品重复提添加数据, 会导致记录重复.因此会触发报错,
报错后就无法进入视图逻辑, 而我们想要实现的操作是重复记录的提交处理成购买数量的增加.而不是给与前端一个报错信息
,因此在序列化组件的时候需要绕过此报错, 对验证处理进行重写,所以使用更灵活的 serializers.Serializer 方式
class ShopCartSerializer(serializers.Serializer):
外键字段处理
Serializer 的外键处理需要用 PrimaryKeyRelatedField 字段, 如果是 ModelSerializer 也可以使用此字段, 但是无需指定 queryset 即可
详情使用见 官网文档 ( ModelSerializer 本来就和数据库有映射, 因此可以自动识别到外联表)
goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())
ModelSerializer 对外键的处理还可以使用 序列化组价的嵌套来处理, 也可以实现相同的效果,
class CategorySerializer(serializers.ModelSerializer):
sub_cat = CategorySerializer2(many=True) class Meta:
model = GoodsCategory
fields = "__all__" class GoodsSerializer(serializers.ModelSerializer):
category = CategorySerializer()
images = GoodsImageSerializer(many=True) class Meta:
model = Goods
fields = "__all__"
重写create
为了处理重复记录的问题, 视图类中我们继承的是 viewsets.ModelViewSet ,但是底层的处理方法是 mixin.CreateModelMixin 中的 create 方法
因此我们需要重写此方法, 当然不能再视图类中重写, 前面分析过了, 在序列化组件验证的时候就会被报错拦截下来. 根本进不去视图类, 重写也没用
因此我们在 序列化组件中重写 create 方法
def create(self, validated_data):
user = self.context["request"].user
nums = validated_data["nums"]
goods = validated_data["goods"] existed = ShoppingCart.objects.filter(user=user, goods=goods) # 判断当前是否已有记录
if existed:
existed = existed[0]
existed.nums += nums
existed.save()
else:
existed = ShoppingCart.objects.create(**validated_data)
# 需要返回保存数据
return existed
重写 update 方法
Serializer 本身是继承自 BaseSerializer , 而 BaseSerializer 中有一个 update 方法
此 update 方法中仅仅是抛出了一个异常
而 Serializer 内部也没有对 update 方法进行重写. 因此导致无法进行更新操作
因此我们需要重写此方法
按照正常的购物流程来说
修改商品应该是先加入购物车才可以进行选择
此处的修改只允许修改商品数量
因此进行如下重写即可
def update(self, instance, validated_data):
# 修改商品数量
instance.nums = validated_data["nums"]
instance.save()
return instance
ps: 对比 ModelSerializer
ModelSerializer 中就有对 update 的重写. 因此不需要额外操作
ps: DELETE 处理
删除操作不需要重写的. BaseSerializer 里面没有对 delete 的操作, 因此也不会有什么奇怪的报错,
这部分的详细问题就进 rest_framework.serializers.py 文件中查看即可
购物商品详情
购物商品详情序列化组件
引用 商品序列化组件来获取商品所有信息
# 购物车商品详情
class ShopCartDetailSerializer(serializers.ModelSerializer):
goods = GoodsSerializer(many=False, read_only=True) class Meta:
model = ShoppingCart
fields = ("goods", "nums")
购物车商品详情视图分流
# 分流 序列化组件
def get_serializer_class(self):
if self.action == 'list':
return ShopCartDetailSerializer
else:
return ShopCartSerializer
购物车全部代码
购物车序列化组件
# 购物车商品详情
class ShopCartDetailSerializer(serializers.ModelSerializer):
goods = GoodsSerializer(many=False, read_only=True) class Meta:
model = ShoppingCart
fields = ("goods", "nums") # 购物车
class ShopCartSerializer(serializers.Serializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
nums = serializers.IntegerField(required=True, label="数量", min_value=1,
error_messages={
"min_value": "商品数量不能小于一",
"required": "请选择购买数量"
})
# Serializer 的外键处理需要用此字段, 如果是 ModelSerializer 也可以使用此字段, 但是无需指定 queryset 即可
goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all()) def create(self, validated_data):
user = self.context["request"].user
nums = validated_data["nums"]
goods = validated_data["goods"] existed = ShoppingCart.objects.filter(user=user, goods=goods) # 判断当前是否已有记录
if existed:
existed = existed[0]
existed.nums += nums
existed.save()
else:
existed = ShoppingCart.objects.create(**validated_data)
# 需要返回保存数据
return existed def update(self, instance, validated_data):
# 修改商品数量
instance.nums = validated_data["nums"]
instance.save()
return instance
购物车视图
# 购物车
class ShoppingCartViewset(viewsets.ModelViewSet):
"""
list:
获取购物车详情
create:
加入购物车
delete:
删除购物记录
"""
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
serializer_class = ShopCartSerializer
# 我们修改的是要的是 goods 的id 而不是这条记录本身的 id
lookup_field = "goods_id" # 分流 序列化组件
def get_serializer_class(self):
if self.action == 'list':
return ShopCartDetailSerializer
else:
return ShopCartSerializer def get_queryset(self):
return ShoppingCart.objects.filter(user=self.request.user)
订单
模型表
订单信息模型表
class OrderInfo(models.Model):
ORDER_STATUS = (
("TRADE_SUCCESS", "成功"),
("TRADE_CLOSED", "超时关闭"),
("WAIT_BUYER_PAY", "交易创建"),
("TRADE_FINISHED", "交易结束"),
("paying", "待支付"),
) user = models.ForeignKey(User, verbose_name="用户")
order_sn = models.CharField(max_length=30, null=True, blank=True, unique=True, verbose_name="订单号")
trade_no = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name=u"交易号")
pay_status = models.CharField(choices=ORDER_STATUS, default="paying", max_length=30, verbose_name="订单状态")
post_script = models.CharField(max_length=200, verbose_name="订单留言")
order_mount = models.FloatField(default=0.0, verbose_name="订单金额")
pay_time = models.DateTimeField(null=True, blank=True, verbose_name="支付时间") # 用户信息
address = models.CharField(max_length=100, default="", verbose_name="收货地址")
signer_name = models.CharField(max_length=20, default="", verbose_name="签收人")
singer_mobile = models.CharField(max_length=11, verbose_name="联系电话") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta:
verbose_name = u"订单"
verbose_name_plural = verbose_name def __str__(self):
return str(self.order_sn)
order_sn
特别说明一下 order_sn 订单号字段, 订单号是必须要有的, 但是是需要在后端进行生成的
且订单号也应该是唯一的, 因此不能使用默认值的方式来处理, 用户在前端生成订单的时候必然是不知道订单号的
但是在 create 的时候会进行所有字段验证, 如果此字段不存在就会报错
为了避免报错, 这里姑且设置为空方便后续操作
trade_no
支付宝提供的交易号
用户信息
用户信息的相关的字段是不能使用外键来处理的, 因为订单的信息是不能随便改动的
如果下订单后, 用户又在用户中心操作了相关的属性也会因为是外键的关系导致订单中的信息也发送变化
因此此处的用户信息需要进行额外的字段来保存
订单商品详情模型表
订单和商品之间是多对多关系, 因此需要第三张表来建立, 同时在此表中需要额外字段商品数量以及添加时间
不能简单的直接通过ORM 的属性来创建
# 订单的商品详情
class OrderGoods(models.Model):
order = models.ForeignKey(OrderInfo, verbose_name="订单信息", related_name="goods")
goods = models.ForeignKey(Goods, verbose_name="商品")
goods_num = models.IntegerField(default=0, verbose_name="商品数量") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta:
verbose_name = "订单商品"
verbose_name_plural = verbose_name def __str__(self):
return str(self.order.order_sn)
订单序列化组件
订单
# 订单
class OrderSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
) # 订单的某些信息是不能自己修改的
pay_status = serializers.CharField(read_only=True)
trade_no = serializers.CharField(read_only=True)
order_sn = serializers.CharField(read_only=True)
pay_time = serializers.DateTimeField(read_only=True) # 生成订单号函数
def generate_order_sn(self):
# 当前时间+userid+随机数
from time import strftime
from random import Random
random_ins = Random()
order_sn = "{time_str}{userid}{ranstr}".format(time_str=strftime("%Y%m%d%H%M%S"),
userid=self.context["request"].user.id,
ranstr=random_ins.randint(10, 99))
return order_sn # 对订单号进行生成
def validate(self, attrs):
attrs["order_sn"] = self.generate_order_sn()
return attrs class Meta:
model = OrderInfo
fields = "__all__"
订单详情
# 订单详情
class OrderDetailSerializer(serializers.ModelSerializer):
goods = OrderGoodsSerialzier(many=True) class Meta:
model = OrderInfo
fields = "__all__"
订单商品
# 订单商品详情
class OrderGoodsSerialzier(serializers.ModelSerializer):
goods = GoodsSerializer(many=False) class Meta:
model = OrderGoods
fields = "__all__"
订单视图
源码剖析
订单视图需要生成订单号, 订单号的生成需要在 保存操作之前,
通过源码翻找可以找到, 在mixin.CreateModelMixin中的 perform_create 方法处理相关的保存操作
订单号生成
所以在保存操作前进行订单号的生成即可, 订单号的生成可以在视图进行完成也可以在序列化组件进行完成,
这里采用的是在序列化组件中进行生成了. ( 注意序列化组件中和视图中取当前用户对象的方式是不同的 )
完成订单号的生成后然后对 perform_create 进行重写
重写创建函数
重写 perform_create 方法, 手动进行订单表的创建添加操作
def perform_create(self, serializer):
order = serializer.save()
shop_carts = ShoppingCart.objects.filter(user=self.request.user)
for shop_cart in shop_carts:
order_goods = OrderGoods()
order_goods.goods = shop_cart.goods
order_goods.goods_num = shop_cart.nums
order_goods.order = order
order_goods.save()
shop_cart.delete()
return order
订单全部代码
订单序列化组件
# 订单商品详情
class OrderGoodsSerialzier(serializers.ModelSerializer):
goods = GoodsSerializer(many=False) class Meta:
model = OrderGoods
fields = "__all__" # 订单详情
class OrderDetailSerializer(serializers.ModelSerializer):
goods = OrderGoodsSerialzier(many=True) class Meta:
model = OrderInfo
fields = "__all__" # 订单
class OrderSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
) # 订单的某些信息是不能自己修改的
pay_status = serializers.CharField(read_only=True)
trade_no = serializers.CharField(read_only=True)
order_sn = serializers.CharField(read_only=True)
pay_time = serializers.DateTimeField(read_only=True) # 生成订单号函数
def generate_order_sn(self):
# 当前时间+userid+随机数
from time import strftime
from random import Random
random_ins = Random()
order_sn = "{time_str}{userid}{ranstr}".format(time_str=strftime("%Y%m%d%H%M%S"),
userid=self.context["request"].user.id,
ranstr=random_ins.randint(10, 99))
return order_sn # 对订单号进行生成
def validate(self, attrs):
attrs["order_sn"] = self.generate_order_sn()
return attrs class Meta:
model = OrderInfo
fields = "__all__"
订单视图全部代码
# 订单
class OrderViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin,
viewsets.GenericViewSet):
"""
list:
获取个人订单
delete:
删除订单
create:
新增订单
"""
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
serializer_class = OrderSerializer def get_queryset(self):
return OrderInfo.objects.filter(user=self.request.user) def get_serializer_class(self):
if self.action == "retrieve":
return OrderDetailSerializer
return OrderSerializer def perform_create(self, serializer):
order = serializer.save()
shop_carts = ShoppingCart.objects.filter(user=self.request.user)
for shop_cart in shop_carts:
order_goods = OrderGoods()
order_goods.goods = shop_cart.goods
order_goods.goods_num = shop_cart.nums
order_goods.order = order
order_goods.save() shop_cart.delete()
return order
DRF 商城项目 - 购物( 购物车, 订单, 支付 )逻辑梳理的更多相关文章
- django 商城项目之购物车以及python中的一些redis命令
最近在用django restframe框架做一个商城项目,有一个关于购物车的业务逻辑,是用cookie和redis存储的购物车信息,在这里记录一下. 完成一个商城项目,如果不做一个购物车,就是十分可 ...
- DRF 商城项目 - 用户操作(收藏, 留言, 收货地址)
个人收藏 整体逻辑类似于 个人中心 ( 个人中心的相关逻辑梳理详情 点击这里 ) 也是两个序列化组价的分流 查看收藏 ( list ) 详情指向 收藏详情 的组价 创建收藏 ( create ) ...
- DRF 商城项目 - 用户( 登录, 注册,登出,个人中心 ) 逻辑梳理
用户登录 自定义用户登录字段处理 用户的登录时通过 手机号也可以进行登录 需要重写登录验证逻辑 from django.contrib.auth.backends import ModelBacken ...
- DRF 商城项目 - 日志处理
logging 模块 logging 模块是最基本的日志处理模块 缺陷 但是拥有一些很致命的缺陷 要求用户主动查询, 需要登录到服务器才可以查看日志文件 自带的报错外部通知也没办法判断同类取舍, 短 ...
- 商城项目的购物车模块的实现------通过session实现
1.新建购物车的实体类Cart public class Cart implements java.io.Serializable{ private Shangpin shangpin;//存放商品实 ...
- 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第十一天】(购物车+订单)
https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...
- 【SSH网上商城项目实战21】从Demo中看易宝支付的流程
转自: https://blog.csdn.net/eson_15/article/details/51447492 这一节我们先写一个简单点的Demo来测试易宝支付的流程,熟悉这个流程后, ...
- 【SSH网上商城项目实战20】在线支付平台的介绍
转自:https://blog.csdn.net/eson_15/article/details/51441431 之前已经完成了首页的显示,用户添加购物车,确认订单等功能,下面就是支付功能的开发了. ...
- 【SSH网上商城项目实战23】完成在线支付功能
转自: https://blog.csdn.net/eson_15/article/details/51464415 上一节我们做好了支付页面的显示,从上一节支付页面显示的jsp代码中可以看出,当用 ...
随机推荐
- json格式处理及扩展
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script> < ...
- DevOps实例
DevOps实例 ------------------------------------------------------------------ 今天先到这儿,希望对您DevOPS, 技术领导力 ...
- js + 加号 报错,IIS 配置
一.问题描述: 1开发环境完全没有问题: 2 build 后生成的js脚本,带有+号. 程序发布到IIS后,带加+号js脚本报错. 二.解决方案 1 修改build规则,让它不产生特殊符号. 能力有 ...
- java实现 批量转换文件编码格式
一.场景说明 不知道大家有没有遇到过之前项目是GBK,现在需要全部换成UTF-8的情况.反正我是遇到了. eclipse可以改变项目的编码格式,但是文件如果直接转换的话里面的中文就会全部乱码,需要先复 ...
- java 学习必备的软件,持续更新中
小编会持续更新在学习Java过程中需要的软件以及各种文件: 话不多说,看行动! 一:JDK (1)JDK1.8(*64): 链接:https://pan.baidu.com/s/1vM0jNXn2CT ...
- Java UrlRewriter伪静态技术运用深入分析
通常我们为了更好的缓解服务器压力,和增强搜索引擎的友好面,都将文章内容生成静态页面. 但是有时为了能实时的显示一些信息,或者还想运用动态脚本解决一些问题,不能用静态的方式来展示网站内容,必须用到动态页 ...
- java:数据结构(二)栈的应用(括号匹配)
一.什么是括号匹配: 括号匹配就是利用计算机辨别表达式里面的括号是否书写成功 例如: {()((a)) }这就是一个正确 (()() 这就是一个错误的 二.括号匹配的算法: 众所周知,括号分为花括 ...
- 从0开始的Python学习016异常
简介 当你的程序不能正常运行的时候,Python会在控制台打印一段提醒,告诉你一个错误,这个错误就是异常. 错误 我在控制台写了一段无效的代码,将print()的括号去掉,在执行这条语句的时候,系统提 ...
- k8s部署dashboard:v1.5.1
1.准备dashboard.yaml文件 apiVersion: extensions/v1beta1 kind: Deployment metadata: # Keep the name in sy ...
- mysql export mysqldump version mismatch upgrade or downgrade your local MySQL client programs
I use MySQL Community Edition and I solved this problem today. goto https://dev.mysql.com/downloads/ ...