商品管理
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)