SKU表管理之保存SKU表数据

保存SKU表数据

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

1、获取三级分类信息

接口分析

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

    # -------获取三级分类信息--------
    url(r'skus/categories/$', skus.SKUCategorieView.as_view()),

 【图片中重写的方法名,作用相同于下面写的类方法】

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

返回数据: JSON

[
        {
            "id": "商品分类id",
            "name": "商品分类名称"
        },
        ...
    ]
返回值类型是否必须说明
Idint商品分类id
name数组商品分类名称

后端实现

from rest_framework.viewsets import ModelViewSet
from meiduo_admin.serializers.skus import SKUGoodsSerializer
from meiduo_admin.utils import UserPageNum
from goods.models import SKU
from rest_framework.generics import ListAPIView
from meiduo_admin.serializers.skus import SKUCategorieSerializer
from goods.models import 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
from goods.models import GoodsCategory


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

2、获取spu表名称数据

接口分析

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

    # -------获取spu表名称数据--------
    url(r'goods/simple/$', skus.SPUSimpleView.as_view()),  # 此行代码和上面规格路由表的作用是一样的

请求参数: 通过请求头传递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/

    # -------获取spu商品规格信息--------
    url(r'goods/(?P<pk>\d+)/specs/$', skus.SPUSpecView.as_view()),

请求参数: 通过请求头传递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'])

定义序列化器

from goods.models import GoodsCategory, SpecificationOption, SPUSpecification

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/

#  sku表查询路由****************************
router = DefaultRouter()
router.register('skus', skus.SKUGoodsView, base_name='sku')
urlpatterns += router.urls

请求参数: 通过请求头传递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)

序列化器的定义

from django.db import transaction
from celery_tasks.static_file.tasks import get_detail_html

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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值