商品管理

1.规格表管理

在规格表中我们需要对规格表数据进行增删改查操作,这时候我们可以借助于视图集中的ModelViewset来完成相应的操作

 

 

1.查询获取规格表列表数据

接口分析

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

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

返回数据: JSON

 {
        "counts": "SPU商品规格总数量",
        "lists": [
            {
                "id": "规格id",
                "name": "规格名称",
                "spu": "SPU商品名称",
                "spu_id": "SPU商品id"
            },
            ...
          ],
          "page": "页码",
          "pages": "总页数",
          "pagesize": "页容量"
      }
返回值类型是否必须说明
count int 总量
lists 数组 规格表信息
page int 页码
pages int 总页数
pagesize int 页容量

后端实现

class SpecsView(ModelViewSet):

    serializer_class = SPUSpecificationSerializer
    queryset = SPUSpecification.objects.all()
    pagination_class = PageNum

 

序列化器的定义

class SPUSpecificationSerializer(serializers.ModelSerializer):
      # 关联嵌套返回spu表的商品名
    spu=serializers.StringRelatedField(read_only=True) 
    # 返回关联spu的id值
    spu_id=serializers.IntegerField()

    class Meta:
        model = SPUSpecification # 商品规格表关联了spu表的外键spu
        fields='__all__'

 

2.保存规格表数据表数据

接口分析

请求方式:POST /meiduo_admin/goods/specs/

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

参数类型是否必须说明
name str 规格名称
spu_id int SPU商品id

返回数据: JSON

{
        "id": "规格id",
        "name": "规格名称",
        "spu": "SPU商品名称",
        "spu_id": "SPU商品id"
    }
参数类型是否必须说明
id Int 规格id
name Str 规格名称
spu str SPU商品名称
spu_id Int spu商品id

后端实现

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

    serializer_class =SPUSpecificationSerializer
    queryset = SPUSpecification.objects.all()
    pagination_class = PageNum

 

3.更新规格表数据

1、 获取要修改规格表的详情信息

点就修改按钮时,我们需要先获取要修改的规格详情信息

接口分析

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

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

在头部中携带要获取的规格ID

返回数据: JSON

  {
        "id": "规格id",
        "name": "规格名称",
        "spu": "SPU商品名称",
        "spu_id": "SPU商品id"
    }
参数类型是否必须说明
id int 规格 ID
name str 规格名称
spu str SPU商品名称
spu_id int SPU商品id

后端实现

# SpecsView继承的是ModelViewSet 所以获取单一规格逻辑还是使用同一个类视图
class SpecsView(ModelViewSet):
    serializer_class =SPUSpecificationSerializer
    queryset = SPUSpecification.objects.all()
    pagination_class = PageNum

 

2、修改规格表数据

接口分析

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

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

参数类型是否必须说明
name str 规格名称
spu_id int 商品SPU ID

返回数据: JSON

  {
        "id": "规格id",
        "name": "规格名称",
        "goods": "SPU商品名称",
        "goods_id": "SPU商品id"
    }
参数类型是否必须说明
id int 规格 ID
name str 规格名称
spu str SPU商品名称
spu_id int SPU商品id

后端实现

# SpecsView继承的是ModelViewSet 所以修改逻辑还是使用同一个类视图
class SpecsView(ModelViewSet):
        """
            规格表视图
        """
    serializer_class =SPUSpecificationSerializer
    queryset = SPUSpecification.objects.all()
    pagination_class = PageNum

 

4.删除规格表数据

接口分析

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

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

在路径中携带删除的规格的id值

返回数据: JSON

返回空

后端实现

# SpecsView继承的是ModelViewSet 所以删除逻辑还是使用同一个类视图
class SpecsView(ModelViewSet):
        """
            规格表视图
        """
    serializer_class =SPUSpecificationSerializer
    queryset = SPUSpecification.objects.all()
    pagination_class = PageNum

 

2.图片管理

在图片表中我们需要对图片数据进行增删改查操作,这时候我们可以借助于视图集中的ModelViewset来完成相应的操作

 

 

1.获取图片列表数据

接口分析

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

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

返回数据: JSON

{
        "counts": "图片总数量",
        "lists": [
              {
                "id": "图片id",
                "sku": "SKU商品id",
                "image": "图片地址"
              }
            ...
       ],
       "page": "页码",
       "pages": "总页数",
       "pagesize": "页容量"
  }
返回值类型是否必须说明
count int 图片总量
lists 数组 图片信息
page int 页码
pages int 总页数
pagesize int 页容量

后端实现

class ImageView(ModelViewSet):
      # 图片序列化器
    serializer_class = ImageSeriazlier
    # 图片查询集
    queryset = SKUImage.objects.all()
    # 分页
    pagination_class = PageNum

序列化器的定义

class ImageSeriazlier(serializers.ModelSerializer):
      # 返回图片关联的sku的id值
    sku=serializers.PrimaryKeyRelatedField(read_only=True)
    class Meta:
        model=SKUImage
        fields=('sku','image','id')

 

2.保存图片数据

在保存数据之前我们需要先获取图片关联的sku的id

 

 

1、获取sku表id

接口分析

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

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

返回数据: JSON

 [
        {
            "id": 1,
            "name": "Apple MacBook Pro 13.3英寸笔记本 银色"
        },
        {
            "id": 2,
            "name": "Apple MacBook Pro 13.3英寸笔记本 深灰色"
        },
        ......
    ]
返回值类型是否必须说明
Id int sku商品id
name 数组 Sku商品名称

后端实现

class ImageView(ModelViewSet):
    serializer_class = ImageSeriazlier
    queryset = SKUImage.objects.all()
    pagination_class = PageNum

        # 获取sku商品信息
    def simple(self,request):
        data = SKU.objects.all()
        ser = SKUSeriazlier(data,many=True)
        return Response(ser.data)

序列化器的定义

class SKUSeriazlier(serializers.ModelSerializer):
    class Meta:
        model=SKU
        fields=('id','name')

 

2、保存图片数据

接口分析

请求方式:POST /meiduo_admin/skus/images/

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

表单提交数据:
        "sku": "SKU商品id",
        "image": "SKU商品图片"
参数类型是否必须说明
sku str SKU商品id
image Fiel SKU商品图片

返回数据: JSON

 {
        "id": "图片id",
        "sku": "SKU商品id",
        "image": "图片地址"
    }
参数类型是否必须说明
id Int 图片id
sku int SKU商品id
image str 图片地址

后端实现

在保存图片的同时,我们还需要异步生成新的详情页页面,这是我们需要定义异步任务

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())

 

视图代码

class ImageView(ModelViewSet):
    serializer_class = ImageSeriazlier
    queryset = SKUImage.objects.all()
    pagination_class = PageNum
        # 获取关联的sku表数据
    def simple(self, request):
        data = SKU.objects.all()
        ser = SKUSeriazlier(data, many=True)
        return Response(ser.data)

    # 重写拓展类的保存业务逻辑
    def create(self, request, *args, **kwargs):
          # 创建FastDFS连接对象
        client = Fdfs_client(settings.FASTDFS_PATH)
        # 获取前端传递的image文件
        data = request.FILES.get('image')
        # 上传图片到fastDFS
        res = client.upload_by_buffer(data.read())
        # 判断是否上传成功
        if res['Status'] != 'Upload successed.':
            return Response(status=403)
        # 获取上传后的路径
        image_url = res['Remote file_id']
        # 获取sku_id
        sku_id = request.data.get('sku')[0]
        # 保存图片
        img = SKUImage.objects.create(sku_id=sku_id, image=image_url)

        # 生成新的详情页页面
        get_detail_html.delay(img.sku.id)
                # 返回结果
        return Response(
            {
                'id': img.id,
                'sku': sku_id,
                'image': img.image.url
            },
            status=201  # 前端需要接受201状态
        )

 

3.更新SKU表数据

1、 获取修改图片的详情信息

点就修改按钮时,我们需要先获取要修改的图片详情信息

接口分析

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

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

在头部中携带要获取的图片商品ID

返回数据: JSON

 {
        "id": "图片id",
        "sku": "SKU商品id",
        "image": "图片地址"
    }
参数类型是否必须说明
id int "图片id"
sku int SKU商品id
iamge str 图片地址

后端实现


# ImageView继承的是ModelViewSet 所以还是使用同一个类视图
class ImageView(ModelViewSet):
    serializer_class = ImageSeriazlier
    queryset = SKUImage.objects.all()
    pagination_class = PageNum
        # 获取关联的sku表数据
    def simple(self, request):
        data = SKU.objects.all()
        ser = SKUSeriazlier(data, many=True)
        return Response(ser.data)

    # 重写拓展类的保存业务逻辑
    def create(self, request, *args, **kwargs):
          # 创建FastDFS连接对象
        client = Fdfs_client(settings.FASTDFS_PATH)
        # 获取前端传递的image文件
        data = request.FILES.get('image')
        # 上传图片到fastDFS
        res = client.upload_by_buffer(data.read())
        # 判断是否上传成功
        if res['Status'] != 'Upload successed.':
            return Response(status=403)
        # 获取上传后的路径
        image_url = res['Remote file_id']
        # 获取sku_id
        sku_id = request.data.get('sku')[0]
        # 保存图片
        image = SKUImage.objects.create(sku_id=sku_id, image=image_url)
                # 返回结果
        return Response(
            {
                'id': image.id,
                'sku': sku_id,
                'image': image.image.url
            },
            status=201  # 前端需要接受201状态码
        )

 

2、更新图片

接口分析

请求方式: PUT /meiduo_admin/skus/images/(?P<pk>\d+)/

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

表单提交数据:
        "sku": "SKU商品id",
        "image": "SKU商品图片"
参数类型是否必须说明
sku str SKU商品id
image Fiel SKU商品图片

返回数据: JSON

 {
        "id": "图片id",
        "sku": "SKU商品id",
        "image": "图片地址"
    }
参数类型是否必须说明
id Int 图片id
sku int SKU商品id
image str 图片地址

后端实现

from django.conf import settings
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from fdfs_client.client import Fdfs_client

from meiduo_admin.serializers.image import ImageSeriazlier, SKUSeriazlier
from meiduo_admin.utils.pagenum import PageNum
from goods.models import SKUImage, SKU


class ImageView(ModelViewSet):
    serializer_class = ImageSeriazlier
    queryset = SKUImage.objects.all()
    pagination_class = PageNum

    def simple(self, request):
        data = SKU.objects.all()
        ser = SKUSeriazlier(data, many=True)
        return Response(ser.data)
        # 重写拓展类的保存业务逻辑
    def create(self, request, *args, **kwargs):
        client = Fdfs_client(settings.FASTDFS_PATH)
        data = request.FILES.get('image')
        res = client.upload_by_buffer(data.read())
        if res['Status'] != 'Upload successed.':
            return Response(status=403)
        image_url = res['Remote file_id']
        sku_id = request.data.get('sku')[0]
        image = SKUImage.objects.create(sku_id=sku_id, image=image_url)

        return Response(
            {
                'id': image.id,
                'sku': sku_id,
                'image': image.image.url
            },
            status=201
        )
        # 重写拓展类的更新业务逻辑
    def update(self, request, *args, **kwargs):

        # 创建FastDFS连接对象
        client = Fdfs_client(settings.FASTDFS_PATH)
        # 获取前端传递的image文件
        data = request.FILES.get('image')
        # 上传图片到fastDFS
        res = client.upload_by_buffer(data.read())
        # 判断是否上传成功
        if res['Status'] != 'Upload successed.':
            return Response(status=403)
        # 获取上传后的路径
        image_url = res['Remote file_id']
        # 获取sku_id
        sku_id = request.data.get('sku')[0]
        # 查询图片对象
        img=SKUImage.objects.get(id=kwargs['pk'])
        # 更新图片
        img.image=image_url
        img.save()

           # 生成新的详情页页面
        get_detail_html.delay(img.sku.id)
        # 返回结果
        return Response(
            {
                'id': img.id,
                'sku': sku_id,
                'image': img.image.url
            },
            status=201  # 前端需要接受201状态码
        )

 

4.删除SKU表数据

接口分析

请求方式: Delte /meiduo_admin/skus/images/(?P<pk>\d+)/

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

在路径中携带删除的图片的id值

返回数据: JSON

返回空

后端实现

#ImageView继承的是ModelViewSet 所以删除逻辑还是使用同一个类视图
class ImageView(ModelViewSet):
      # 图片序列化器
    serializer_class = ImageSeriazlier
    # 图片查询集
    queryset = SKUImage.objects.all()
    # 分页
    pagination_class = PageNum

 

3.SKU表管理

在sku表中我们需要对SKU表数据进行增删改查操作,这时候我们可以借助于视图集中的ModelViewset来完成相应的操作

 

 

1.查询获取sku表列表数据

在获取sku数据时,我们在请求中包含了查询关键keyword,这时我么就需要对keyword进行判断,来返回不同的查询数据

接口分析

请求方式: GET /meiduo_admin/skus/?keyword=<名称|副标题>&page=<页码>&page_size=<页容量>

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

返回数据: JSON

{
        "counts": "商品SPU总数量",
        "lists": [
            {
                "id": "商品SKU ID",
                "name": "商品SKU名称",
                "spu": "商品SPU名称",
                "spu_id": "商品SPU ID",
                "caption": "商品副标题",
                "category_id": "三级分类id",
                "category": "三级分类名称",
                "price": "价格",
                "cost_price": "进价",
                "market_price": "市场价格",
                "stock": "库存",
                "sales": "销量",
                "is_launched": "上下架",
                "specs": [
                    {
                        "spec_id": "规格id",
                        "option_id": "选项id"
                    },
                    ...
                ]
            },
            ...
          ],
            "page": "页码",
            "pages": "总页数",
            "pagesize": "页容量"
      }
返回值类型是否必须说明
count int SKUs商总量
lists 数组 SKU信息
page int 页码
pages int 总页数
pagesize int 页容量

后端实现

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAdminUser
from datetime import date

from users.models import User
from goods.models import GoodsVisitCount


class SKUGoodsView(ModelViewSet):
        # 指定序列化器
    serializer_class =SKUGoodsSerializer
        # 指定分页器 进行分页返回
    pagination_class = PageNum
        # 重写get_queryset方法,判断是否传递keyword查询参数
    def get_queryset(self):
          # 提取keyword
        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 SKUSpecificationSerialzier(serializers.ModelSerializer):
      """
          SKU规格表序列化器
        """
    spec_id = serializers.IntegerField(read_only=True)
    option_id = serializers.IntegerField(read_only=True)
    class Meta:
        model = SKUSpecification # SKUSpecification中sku外键关联了SKU表
        fields=("spec_id",'option_id')


class SKUGoodsSerializer(serializers.ModelSerializer):
      """ 
          获取sku表信息的序列化器
      """
      # 指定所关联的选项信息 关联嵌套返回
    specs = SKUSpecificationSerialzier(read_only=True,many=True)
    # 指定分类信息
    category_id = serializers.IntegerField()
    # 关联嵌套返回
    category = serializers.StringRelatedField(read_only=True)
    # 指定所关联的spu表信息
    spu_id = serializers.IntegerField()
    # 关联嵌套返回
    spu = serializers.StringRelatedField(read_only=True)
    class Meta:
        model = SKU  # SKU表中category外键关联了GoodsCategory分类表。spu外键关联了SPU商品表
        fields='__all__'

 

2.保存SKU表数据

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

 

 

1、获取三级分类信息

接口分析

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

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

返回数据: JSON

[
        {
            "id": "商品分类id",
            "name": "商品分类名称"
        },
        ...
    ]
返回值类型是否必须说明
Id int 商品分类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)
  #categories = GoodsCategory.objects.filter(subs__id=None)

 

序列化器的定义

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名称"
        },
        ...
    ]
返回值类型是否必须说明
Id int 商品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": "选项名称"
                },
                ...
            ]
        },
        ...
   ]
返回值类型是否必须说明
Id int 规格id
name Str 规格名称
Sup str Spu商品名称
Spu_id Int spu商品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数据。

参数类型是否必须说明
name str 商品SKU名称
spu_id int 商品SPU ID
caption str 商品副标题
category_id int 三级分类ID
price int 价格
cost_price int 进价
market_price int 市场价
stock int 库存
is_launched boole 上下架

返回数据: 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"
            },
            ...
        ]
    }
参数类型是否必须说明
name str 商品SKU名称
spu_id int   商品SPU ID
caption str   商品副标题
category_id int   三级分类ID
price int   价格
cost_price int   进价
market_price int   市场价
stock int   库存
is_launched boole   上下架

后端实现:

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

# 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())

 

3.更新SKU表数据

1、 获取修改商品的详情信息

点就修改按钮时,我们需要先获取要修改的商品详情信息

接口分析

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

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

在头部中携带要获取的spu商品ID

返回数据: 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"
            },
            ...
        ]
    }
参数类型是否必须说明
name str 商品SKU名称
spu_id int 商品SPU ID
caption str 商品副标题
category_id int 三级分类ID
price int 价格
cost_price int 进价
market_price int 市场价
stock int 库存
is_launched boole 上下架

后端实现

# SKUGoodsView继承的是ModelViewSet 所以获取单一spu商品逻辑还是使用同一个类视图
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)

 

接口分析

请求方式: PUT meiduo_admin/skus/(?P<pk>\d+)/

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

参数类型是否必须说明
name str 商品SKU名称
spu_id int 商品SPU ID
caption str 商品副标题
category_id int 三级分类ID
price int 价格
cost_price int 进价
market_price int 市场价
stock int 库存
is_launched boole 上下架

返回数据: 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"
            },
            ...
        ]
    }
参数类型是否必须说明
name str 商品SKU名称
spu_id int 商品SPU ID
caption str 商品副标题
category_id int 三级分类ID
price int 价格
cost_price int 进价
market_price int 市场价
stock int 库存
is_launched boole 上下架

后端实现

# SKUGoodsView继承的是ModelViewSet 所以更新逻辑还是使用同一个类视图
class SKUGoodsView(ModelViewSet):
    serializer_class =SKUGoodsSerializer
    pagination_class = PageNum
    def get_queryset(self):
        keyword=self.request.query_params.get('keyword')
        if keyword == '':
            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

    def update(self, instance, validated_data):
        # 获取规格信息
        specs = self.context['request'].data.get('specs')
        # 因为sku表中没有specs字段,所以在保存的时候需要删除validated_data中specs数据
        del validated_data['specs']

        with transaction.atomic():
            # 开启事务
            sid = transaction.savepoint()
            try:
                # 1、更新sku表
                SKU.objects.filter(id=instance.id).update(**validated_data)

                # 2、更新SKU具体规格表
                for spec in specs:
                    SKUSpecification.objects.create(sku=instance, 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(instance.id)
                return instance

 

删除SKU表数据

接口分析

请求方式: Delte meiduo_admin/skus/(?P<pk>\d+)/

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

在路径中携带删除的spu的id值

返回数据: JSON

返回空

后端实现

# SKUGoodsView继承的是ModelViewSet 所以删除逻辑还是使用同一个类视图
class SKUGoodsView(ModelViewSet):
    serializer_class =SKUGoodsSerializer
    pagination_class = PageNum
    def get_queryset(self):
        keyword=self.request.query_params.get('keyword')
        if keyword == '':
            return SKU.objects.all()
        else:
            return SKU.objects.filter(name=keyword)

 

posted @ 2020-08-14 04:07  sewen  Views(517)  Comments(2Edit  收藏  举报