三十、订单列表展示及保存(商品部分)

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值