阿语Python项目实操之美多后台管理-商品管理之sku表管理第5.1.2节保存SKU表数据...

保存SKU表数据

在保存数据之前我们需要先获取三级分类信息、SPU表的名称信息、当前SPU商品的规格选项信息加载到页面中

1、获取三级分类信息

接口分析

请求方式:GET /meiduo_admin/skus/categories/

请求参数:通过请求头传递jwt token数据。

返回数据:JSON

[
        {
            "id": "商品分类id",
            "name": "商品分类名称"
        },
        ...
    ]
返回值类型是否必须说明
Idint商品分类id
name数组商品分类名称
后端实现
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import ListAPIView

from meiduo_admin.serializers.goods import SKUGoodsSerializer, SKUCategorieSerializer
from meiduo_admin.utils.pagenum import PageNum
from goods.models import SKU,GoodsCategory

class SKUCategorieView(ListAPIView):

    serializer_class = SKUCategorieSerializer
    # 根据数据存储规律parent_id大于37为三级分类信息,查询条件为parent_id__gt=37
    queryset = GoodsCategory.objects.filter(parent_id__gt=37)

序列化器的定义

from rest_framework import serializers
from goods.models import SKU,GoodsCategory
from goods.models import SKUSpecification

class SKUCategorieSerializer(serializers.ModelSerializer):
  """
      商品分类序列化器
  """
    class Meta:
        model = GoodsCategory
        fields = "__all__"

2、获取spu表名称数据

接口分析

请求方式:GET /meiduo_admin/goods/simple/

请求参数:通过请求头传递jwt token数据。

返回数据:JSON

[
        {
            "id": "商品SPU ID",
            "name": "SPU名称"
        },
        ...
    ]
返回值类型是否必须说明
Idint商品SPU ID
name数组SPU名称
后端实现
class SPUSimpleView(ListAPIView):
    serializer_class = SPUSimpleSerializer
    queryset = SPU.objects.all()

定义序列化器

class SPUSimpleSerializer(serializers.ModelSerializer):
      """
          商品SPU表序列化器
      """ 
    class Meta:
        model=GoodsCategory
        fields = ('id','name')

3、获取SPU商品规格信息

接口分析

请求方式:GET meiduo_admin/goods/(?P<pk>\d+)/specs/

请求参数:通过请求头传递jwt token数据。

在路径中传递当前SPU商品id

返回数据:JSON

 [
        {
            "id": "规格id",
            "name": "规格名称",
            "spu": "SPU商品名称",
            "spu_id": "SPU商品id",
            "options": [
                {
                    "id": "选项id",
                    "name": "选项名称"
                },
                ...
            ]
        },
        ...
   ]
返回值类型是否必须说明
Idint规格id
nameStr规格名称
SupstrSpu商品名称
Spu_idIntspu商品id
options
关联的规格选项
后端实现
class SPUSpecView(ListAPIView):

    serializer_class = SPUSpecSerialzier
    # 因为我们继承的是ListAPIView,在拓展类中是通过get_queryset获取数据,但是我们现在要获取的是规格信息,所以重写get_queryset
    def get_queryset(self):
          # 获取spuid值
             pk=self.kwargs['pk']
        # 根据spu的id值关联过滤查询出规格信息
        return SPUSpecification.objects.filter(spu_id=self.kwargs['pk'])

定义序列化器


class SPUOptineSerializer(serializers.ModelSerializer):
  """
      规格选项序列化器
  """
    class Meta:
        model = SpecificationOption
        fields=('id','value')


class SPUSpecSerialzier(serializers.ModelSerializer):
      """
          规格序列化器
      """
    # 关联序列化返回SPU表数据
    spu = serializers.StringRelatedField(read_only=True)
    spu_id = serializers.IntegerField(read_only=True)
    # 关联序列化返回 规格选项信息
    options = SPUOptineSerializer(read_only=True,many=True) # 使用规格选项序列化器
    class Meta:
        model = SPUSpecification # SPUSpecification中的外键spu关联了SPU商品表
        fields="__all__"

4、保存SKU数据

接口分析

请求方式:POST meiduo_admin/skus/

请求参数:通过请求头传递jwt token数据。

参数类型是否必须说明
namestr商品SKU名称
spu_idint商品SPU ID
captionstr商品副标题
category_idint三级分类ID
priceint价格
cost_priceint进价
market_priceint市场价
stockint库存
is_launchedboole上下架

返回数据:JSON

 {
        "id": "商品SKU ID",
        "name": "商品SKU名称",
        "goods": "商品SPU名称",
        "goods_id": "商品SPU ID",
        "caption": "商品副标题",
        "category_id": "三级分类id",
        "category": "三级分类名称",
        "price": "价格",
        "cost_price": "进价",
        "market_price": "市场价",
        "stock": "库存",
        "sales": "销量",
        "is_launched": "上下架",
        "specs": [
            {
                "spec_id": "规格id",
                "option_id": "选项id"
            },
            ...
        ]
    }
参数类型是否必须说明
namestr商品SKU名称
spu_idint
商品SPU ID
captionstr
商品副标题
category_idint
三级分类ID
priceint
价格
cost_priceint
进价
market_priceint
市场价
stockint
库存
is_launchedboole
上下架

后端实现:

在后端实现中,我们需要异步生成详情页静态页面,同时涉及到多张表操作,我们还需要使用事务

# SKUGoodsView继承的是ModelViewSet 所以保存逻辑还是使用同一个类视图
class SKUGoodsView(ModelViewSet):

    serializer_class =SKUGoodsSerializer
    pagination_class = PageNum

    def get_queryset(self):
        keyword=self.request.query_params.get('keyword')
        if keyword == '' or keyword is None:
            return SKU.objects.all()

        else:
            return SKU.objects.filter(name=keyword)

序列化器的定义

class SKUSerializer(serializers.ModelSerializer):
    """
        SKU表数据
    """
    # 返回关联spu表的名称和关联的分类表的名称
    spu = serializers.StringRelatedField(read_only=True)
    category = serializers.StringRelatedField(read_only=True)

    # 返回模型类类的spu_id和category_id
    spu_id = serializers.IntegerField()
    category_id = serializers.IntegerField()

    # 返回商品的规格信息 ,在商品规格详情表(SKUSpecification)中有个外键sku关了当前的SKU表
    specs = SKUSpecificationSerializer(many=True)

    class Meta:
        model = SKU
        fields = "__all__"

    def create(self, validated_data):
      # self指的是当前序列化器对象,在self下面有个context属性保存了请求对象
      specs=self.context['request'].data.get('specs')
      # specs = validated_data['specs']
      # 因为sku表中没有specs字段,所以在保存的时候需要删除validated_data中specs数据
      del validated_data['specs']
      with transaction.atomic():
        # 开启事务
        sid = transaction.savepoint()
        try:
          # 1、保存sku表
          sku = SKU.objects.create(**validated_data)
          # 2、保存SKU具体规格
          for spec in specs:
            SKUSpecification.objects.create(sku=sku, spec_id=spec['spec_id'], option_id=spec['option_id'])
        except:
          # 捕获异常,说明数据库操作失败,进行回滚
          transaction.savepoint_rollback(sid)
          return serializers.ValidationError('数据库错误')
        else:
          # 没有捕获异常,数据库操作成功,进行提交
          transaction.savepoint_commit(sid)
          # 执行异步任务生成新的静态页面
          get_detail_html.delay(sku.id)
          return sku

异步任务:

import os

from django.conf import settings
from django.shortcuts import render

from goods.models import SKU
from meiduo_mall.utils.breadcrumb import get_breadcrumb
from meiduo_mall.utils.categories import get_categories
from celery_tasks.main import app


@app.task(name='get_detail_html')
def get_detail_html(sku_id):
    # 获取当前sku对象
    sku=SKU.objects.get(id=sku_id)
    # 分类数据
    categories = get_categories()

    # 获取面包屑导航
    breadcrumb = get_breadcrumb(sku.category)

    # 获取spu
    spu = sku.spu

    # 获取规格信息:sku===>spu==>specs
    specs = spu.specs.order_by('id')

    # 查询所有的sku,如华为P10的所有库存商品
    skus = spu.skus.order_by('id')
    '''
    {
        选项:sku_id
    }
    说明:键的元组中,规格的索引是固定的
    示例数据如下:
    {
        (1,3):1,
        (2,3):2,
        (1,4):3,
        (2,4):4
    }
    '''
    sku_options = {}
    sku_option = []
    for sku1 in skus:
        infos = sku1.specs.order_by('spec_id')
        option_key = []
        for info in infos:
            option_key.append(info.option_id)
            # 获取当前商品的规格信息
            if sku.id == sku1.id:
                sku_option.append(info.option_id)
        sku_options[tuple(option_key)] = sku1.id

    # 遍历当前spu所有的规格
    specs_list = []
    for index, spec in enumerate(specs):
        option_list = []
        for option in spec.options.all():
            # 如果当前商品为蓝、64,则列表为[2,3]
            sku_option_temp = sku_option[:]
            # 替换对应索引的元素:规格的索引是固定的[1,3]
            sku_option_temp[index] = option.id
            # 为选项添加sku_id属性,用于在html中输出链接
            option.sku_id = sku_options.get(tuple(sku_option_temp), 0)
            # 添加选项对象
            option_list.append(option)
        # 为规格对象添加选项列表
        spec.option_list = option_list
        # 重新构造规格数据
        specs_list.append(spec)

    context = {
        'sku': sku,
        'categories': categories,
        'breadcrumb': breadcrumb,
        'category_id': sku.category_id,
        'spu': spu,
        'specs': specs_list
    }
    response = render(None, 'detail.html', context)
    file_name = os.path.join(settings.BASE_DIR, 'static/detail/%d.html' % sku.id)
    # 写文件
    with open(file_name, 'w') as f1:
        f1.write(response.content.decode())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值