1、创建订单应用orders
python ../../manage.py startapp orders
2、在配置文件dev中注册应用
'orders.apps.OrdersConfig',
3、创建订单模型类
from django.db import models
# Create your models here.
from django.db import models
from meiduo_mall.utils.models import BaseModel
from users.models import User, Address
from goods.models import SKU
# Create your models here.
class OrderInfo(BaseModel):
"""
订单信息
"""
PAY_METHODS_ENUM = {
"CASH": 1,
"ALIPAY": 2
}
PAY_METHOD_CHOICES = (
(1, "货到付款"),
(2, "支付宝"),
)
ORDER_STATUS_ENUM = {
"UNPAID": 1,
"UNSEND": 2,
"UNRECEIVED": 3,
"UNCOMMENT": 4,
"FINISHED": 5
}
ORDER_STATUS_CHOICES = (
(1, "待支付"),
(2, "待发货"),
(3, "待收货"),
(4, "待评价"),
(5, "已完成"),
(6, "已取消"),
)
order_id = models.CharField(max_length=64, primary_key=True, verbose_name="订单号")
user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name="下单用户")
address = models.ForeignKey(Address, on_delete=models.PROTECT, verbose_name="收获地址")
total_count = models.IntegerField(default=1, verbose_name="商品总数")
total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="商品总金额")
freight = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="运费")
pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=1, verbose_name="支付方式")
status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name="订单状态")
class Meta:
db_table = "tb_order_info"
verbose_name = '订单基本信息'
verbose_name_plural = verbose_name
class OrderGoods(BaseModel):
"""
订单商品
"""
SCORE_CHOICES = (
(0, '0分'),
(1, '20分'),
(2, '40分'),
(3, '60分'),
(4, '80分'),
(5, '100分'),
)
order = models.ForeignKey(OrderInfo, related_name='skus', on_delete=models.CASCADE, verbose_name="订单")
sku = models.ForeignKey(SKU, on_delete=models.PROTECT, verbose_name="订单商品")
count = models.IntegerField(default=1, verbose_name="数量")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="单价")
comment = models.TextField(default="", verbose_name="评价信息")
score = models.SmallIntegerField(choices=SCORE_CHOICES, default=5, verbose_name='满意度评分')
is_anonymous = models.BooleanField(default=False, verbose_name='是否匿名评价')
is_commented = models.BooleanField(default=False, verbose_name='是否评价了')
class Meta:
db_table = "tb_order_goods"
verbose_name = '订单商品'
verbose_name_plural = verbose_name
# OrderInfo.objects.create(pay_method=OrderInfo.PAY_METHODS_ENUM['CASH'])
4、迁移数据库
python ../../manage.py makemigrations
python ../../manage.py migrate
5、在orders/views中创建OrderShowView,OrderSaveView类
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import CreateAPIView
from django_redis import get_redis_connection
# Create your views here.
from goods.models import SKU
from decimal import Decimal
from orders.serializers import OrderSerialzier, OrderSaveSerializes
class OrderShowView(APIView):
"""
获取订单商品信息
"""
def get(self,request):
# 1、建立redis连接
conn=get_redis_connection('carts')
# 2、获取用户
user=request.user
# 3、获取hash sku_id和count
sku_id_count=conn.hgetall('cart_%s'%user.id) #{16:2}
cart={}
for sku_id,count in sku_id_count.items():
cart[int(sku_id)]=int(count)
# 4、获取set集合
sku_ids=conn.smembers('cart_selected_%s'%user.id)
# 5、根据集合中的sku_id获取商品对象
skus=SKU.objects.filter(id__in=sku_ids)
# 6、给商品数据对象添加 count
for sku in skus:
sku.count=cart[sku.id]
# 7、指定运费
freight= Decimal(10.00)
# 8、序列化返回
ser=OrderSerialzier({'freight':freight,'skus':skus})
return Response(ser.data)
class OrderSaveView(CreateAPIView):
"""
保存订单
"""
serializer_class = OrderSaveSerializes
6、在orders/serializers中创建序列化器
from decimal import Decimal
from django_redis import get_redis_connection
from rest_framework import serializers
from django.db import transaction
from goods.models import SKU
from orders.models import OrderInfo, OrderGoods
from datetime import datetime
class SKUSerializers(serializers.ModelSerializer):
count = serializers.IntegerField(read_only=True)
class Meta:
model = SKU
fields = "__all__"
class OrderSerialzier(serializers.Serializer):
skus = SKUSerializers(many=True)
freight = serializers.DecimalField(max_digits=10, decimal_places=2)
class OrderSaveSerializes(serializers.ModelSerializer):
class Meta:
model = OrderInfo
fields = ('address', 'pay_method','order_id')
extra_kwargs={
'order_id':{
'read_only':True
},
'address': {
'write_only': True
},
'pay_method': {
'write_only': True
},
}
# @transaction.atomic()
def create(self, validated_data):
# 保存数据
# 1、获取用户
user = self.context['request'].user
# 2、获取地址和支付方式
address = validated_data['address']
pay_method = validated_data['pay_method']
# 3、构建订单编号
order_id = datetime.now().strftime('%Y%m%d%H%M%S') + '%06d' % user.id
with transaction.atomic():
# 创建保存点
save_ponit=transaction.savepoint()
try:
# 4、生成订单基本信息表
order = OrderInfo.objects.create(
order_id=order_id,
user=user,
address=address,
total_count=0,
total_amount=Decimal(0),
freight=Decimal(10),
pay_method=pay_method,
status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else
OrderInfo.ORDER_STATUS_ENUM['UNPAID']
)
# 5、查询选中状态的商品数据对象
conn = get_redis_connection('carts')
# 获取hash sku_id和count
sku_id_count = conn.hgetall('cart_%s' % user.id) # {16:2}
cart = {}
for sku_id, count in sku_id_count.items():
cart[int(sku_id)] = int(count)
# 获取set集合
sku_ids = conn.smembers('cart_selected_%s' % user.id)
# 根据集合中的sku_id获取商品对象
skus = SKU.objects.filter(id__in=sku_ids)
for sku in skus:
old_stock = sku.stock # 原始库存
old_salse = sku.sales # 原始销量
sku_count = cart[sku.id]
# 6、更新sku中库存和销量
new_stock = old_stock - sku_count
new_salse = old_salse + sku_count
sku.stock = new_stock
sku.sales = new_salse
sku.save()
# 7、更新SPU中总销量
sku.goods.sales += sku_count
sku.goods.save()
# 8、更新订单基本信息表中总价和总量
order.total_amount += (sku.price * sku_count)
order.total_count += sku_count
# 9、保存商品订单表
OrderGoods.objects.create(
order=order,
sku = sku,
count = sku_count,
price = sku.price
)
# 更新总价 加上运费
order.total_amount += order.freight
order.save()
except:
transaction.savepoint_rollback(save_ponit)
else:
transaction.savepoint_commit(save_ponit)
# 10、删除缓存中选中状态的商品id
conn.hdel('cart_%s'%user.id,*sku_ids)
conn.srem('cart_selected_%s'%user.id,*sku_ids)
# 11、结果返回
return order
7、注册路由
- 注册主路由
url(r'^', include('orders.urls')),
- 注册子路由
from django.conf.urls import url
from django.contrib import admin
from . import views
urlpatterns = [
url(r'^orders/settlement/$', views.OrderShowView.as_view()),
url(r'^orders/$', views.OrderSaveView.as_view()),
]
8、数据库事务
在保存订单数据中,涉及到多张表(OrderInfo、OrderGoods、SKU)的数据修改,对这些数据的修改应该是一个整体事务,即要么一起成功,要么一起失败。
1>Django中对于数据库的事务,默认每执行一句数据库操作,便会自动提交。我们需要在保存订单中自己控制数据库事务的执行流程。
2>在Django中可以通过django.db.transaction模块提供的atomic来定义一个事务,atomic提供两种用法
- 装饰器用法
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# 这些代码会在一个事务中执行
...
- with语句用法
from django.db import transaction
def viewfunc(request):
# 这部分代码不在事务中,会被Django自动提交
...
with transaction.atomic():
# 这部分代码会在事务中执行
...
在Django中,还提供了保存点=的支持,可以在事务中创建保存点来记录数据的特定状态,数据库出现错误时,可以恢复到数据保存点的状态
from django.db import transaction
# 创建保存点
save_id = transaction.savepoint()
# 回滚到保存点
transaction.savepoint_rollback(save_id)
# 提交从保存点到当前状态的所有数据库事务操作
transaction.savepoint_commit(save_id)