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代码中可以看出,当用 ...
随机推荐
- 利用tornado实现表格文件预览
项目介绍 本文将介绍笔者的一个项目,主要是利用tornado实现表格文件的预览,能够浏览的表格文件支持CSV以及Excel文件.预览的界面如下: 下面我们将看到这个功能是如何通过tornado ...
- CentOS6.8 安装node.js npm
环境:CentOS6.8_X64系统 一.到官方下载最新的编译好的安装文件,目前是6.9.4. $>cd /usr/local/src #定位到这个目录,下载的文件会在这个目录#使用wget下载 ...
- python3 Flask -day2
flask 实战第二天,url传参 当我们访问网站/的时候,会执行hell_world函数,并把这个函数的返回值返回给浏览器,这样浏览器就显示hello world了 @app.route('/') ...
- Docker 创建 Bamboo6.7.1 以及与 Crowd3.3.2 实现 SSO 单点登录
目录 目录 1.介绍 1.1.什么是 Bamboo? 2.Bamboo 的官网在哪里? 3.如何下载安装? 4.对 Bamboo 进行配置 4.1.获取授权许可 4.2.一般配置 4.3.数据库配置 ...
- Kali Linux入坑之基本配置(2018.1)
我在?天前就决心如Kali的坑,然而安装kali呀vm tools呀更新呀弄了好几天.期间出现的各种问题在此汇总一下. 1.Kali的安装版本选择 在官网上看到的这么多Kali版本应该怎么选呢,在网上 ...
- Pycharm安装并配置jupyter notebook
Pycharm安装并配置jupyter notebook Pycharm安装并配置jupyter notebook 一: 安装命令jupyter: pip install jupyter 如果缺少依赖 ...
- git 同步远程分支
1. 同步远程分支到本地 git fetch 2. 查看本地分支 git branch *dev //当前分支 master test 3.切换分支 git checkout master // 切换 ...
- 【spring源码分析】IOC容器初始化(三)
前言:在[spring源码分析]IOC容器初始化(二)中已经得到了XML配置文件的Document实例,下面分析bean的注册过程. XmlBeanDefinitionReader#registerB ...
- Day 2 下午
[POJ 3468]A Simple Problem with Integers给定Q个数A1, ..., AQ,多次进行以下操作:1.对区间[L, R]中的每个数都加n.2.求某个区间[L, R]中 ...
- php解决微信文章图片防盗链
解决微信文章图片防盗链 function actionWechatImg() { header('Content-type: image/jpg'); $url = $_GET['url']; $re ...