美多第四天
分页查询用户
- 在meiduo_admin 新建文件包utiles—>meiduo_pagination.py
在用户查询 下 写入分页功能- from apps.meiduo_admin.utils.meiduo_pagination import MeiduoPagination
- pagination_class = MeiduoPagination
- 1.自定义分页类型,指定页大小、参数名称、最大页大小
- 2.在视图类中指定分页类型
- 3.通过阅读源码结论:重写分页类型的方法,自定义响应体
- meiduo_pagination.py 写入
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class MeiduoPagination(PageNumberPagination):
#后端指定每页显示数量
page_size = 10
# 允许从查询参数中接收page_参数,用于制定页大小
page_size_query_param = 'page_size'
#如果页的大小参数大于此值,则页大小被设置为此值
max_page_size = 100
def get_paginated_response(self, data):
return Response({
'lists':data,# 用户数据
'counts':self.page.paginator.count,# 总数量
'page':self.page.number,# 当前页数
'pages':self.page.paginator.num_pages,# 总页数
'pagesize':self.page.paginator.per_page, # 后端指定的页容量
})
注意 看一些源码 慢慢理解
创建–增加用户
-
在序列化器中验证
- 在serializer->user中添加模型类
-
1 增加password属性
-
2 指定只读,只写字段
# 只读字段取 'id' read_only_fields = ['id'] extra_kwargs={ 'password':{ 'write_only':True } }
-
3 定义验证方法
def validate(self,value): #前段已经验证过 不用再验证 return value
-
4 重写create()方法:用户的密码需要加密保存
def create(self, validated_data): return User.objects.create_user(**validated_data)
-
规格管理
- 视图类:Modelviewset
- 序列化器类:ModelSerializer
创建文件
- views以及serialazers中创建spec.py
serializer中的spec
from rest_framework import serializers
from apps.goods.models import SPUSpecification
#商品spu规格
class SpecSerlalizer(serializers.ModelSerializer):
spu = serializers.StringRelatedField(read_only=True)
spu_id = serializers.IntegerField()
class Meta:
model = SPUSpecification
fields = ['id','name','spu','spu_id']
read_only_fields = ['id']
views中的spec
from rest_framework.viewsets import ModelViewSet
from apps.goods.models import SPUSpecification
from apps.meiduo_admin.serializers.spec import SpecSerlalizer
from apps.meiduo_admin.utils.meiduo_pagination import MeiduoPagination
class SpecViewSet(ModelViewSet):
queryset = SPUSpecification.objects.all()
serializer_class = SpecSerlalizer
pagination_class = MeiduoPagination
路由规格
router = SimpleRouter()
router.register('goods/specs',spec.SpecViewSet,base_name ='specs')
urlpatterns+=router.urls
在serializers和views中创建spu.py
- serializers中的spu
from rest_framework import serializers
from apps.goods.models import SPU
class SpuSimpleSeralizer(serializers.ModelSerializer):
class Meta:
model = SPU
fields = ['id','name']
- views中的spu
from rest_framework.generics import ListAPIView
from apps.meiduo_admin.serializers.spu import SpuSimpleSeralizer
from apps.goods.models import SPU
class SpuSimpleView(ListAPIView):
queryset = SPU.objects.all()
serializer_class = SpuSimpleSeralizer
路由规范
url(r'^goods/simple/$', spu.SpuSimpleView.as_view()),
SKU管理
- 视图类:ModelViewSet
- 序列化器类:ModelSerializer
- SKU的查询,过滤,分页
创建文件
-在serializer创建sku.py
from rest_framework import serializers
from apps.goods.models import SKU
class SkuSpecRelatedSerializer(serializers.Serializer):
spec_id = serializers.IntegerField()
option_id = serializers.IntegerField()
class SkuSerializer(serializers.ModelSerializer):
# 将标准商品以字符串输出
spu = serializers.StringRelatedField(read_only=True)
# 指定all时,隐藏属性需要明确定义
spu_id = serializers.IntegerField()
# 指定第三级分类外键输出名称
category = serializers.StringRelatedField(read_only=True)
# 指定隐藏属性
category_id = serializers.IntegerField()
# 规格信息
specs = SkuSpecRelatedSerializer(many=True, read_only=True)
class Meta:
model = SKU
# fields = '__all__'
exclude = ['create_time', 'update_time']
- 在views中创建sku.py
from rest_framework.viewsets import ModelViewSet
from apps.goods.models import SKU
from apps.meiduo_admin.serializers.sku import SkuSerializer
from apps.meiduo_admin.utils.meiduo_pagination import MeiduoPagination
from django.db.models import Q
class SkuViewSet(ModelViewSet):
# queryset = SKU.objects.all().order_by('-id')
def get_queryset(self):
queryset = SKU.objects
# 接收查询参数中的关键字
keyword = self.request.query_params.get('keyword')
if keyword:
# 构造查询条件
# 两个条件为逻辑与关系
# queryset = queryset.filter(name__contains=keyword, caption__contains=keyword)
# 两个条件为逻辑或关系
queryset = queryset.filter(Q(name__contains=keyword) | Q(caption__contains=keyword))
# 排序
queryset = queryset.order_by('-id')
# 查询集
return queryset
serializer_class = SkuSerializer
pagination_class = MeiduoPagination
- 创建视图
router.register('skus',sku.SkuViewSet,base_name='skus')
增加
- 准备
- 查询第三级分类
在serializers中创建category.py
from rest_framework import serializers
from apps.goods.models import GoodsCategory
class Category3Serializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
在views中创建category.py
from rest_framework.generics import ListAPIView
from apps.goods.models import GoodsCategory
from apps.meiduo_admin.serializers.category import Category3Serializer
class Category3View(ListAPIView):
serializer_class = Category3Serializer
# 查询第三级分类
queryset = GoodsCategory.objects.filter(subs__isnull=True)
创建视图
# sku-第三级分类
url('^skus/categories/$', category.Category3View.as_view()),
根据spu_id查询规格及选项
在serializers中创建spec.py
class OptionRelatedSerializer(serializers.Serializer):
id = serializers.IntegerField()
value = serializers.CharField()
class SpecBySpuSerializer(serializers.Serializer):
# 根据标准商品编号,查询规格及选项
id = serializers.IntegerField()
name = serializers.CharField()
spu = serializers.StringRelatedField(read_only=True)
spu_id = serializers.IntegerField()
# 规格与选项为1对多关系,在选项中定义了规格外键,通过参数related_name指定options
options = OptionRelatedSerializer(many=True, read_only=True)
在views中创建spec.py
class SpecBySpuView(ListAPIView):
# def get(self,request,*args,**kwargs):
# 根据标准商品编号查询规格及选项
serializer_class = SpecBySpuSerializer
# 需要从路径中提取参数,拼接查询条件
def get_queryset(self):
# 视图对象的属性kwargs表示从路径中提取的关键字参数
return SPUSpecification.objects.filter(spu_id=self.kwargs.get('spu_id'))
创建视图
# 根据spu查询spec的路由goods/1/specs/
url('^goods/(?P<spu_id>\d+)/specs/$', spec.SpecBySpuView.as_view()),
结果:
- 在sku表中添加数据
- 在sku规格表中添加数据
- 生成静态文件
- 实现:重写序列化器的create()方法
- 注意:将模板引擎改为jinja2
- 注意:修改模板文件,将图片输出进行判断
在serializers的sku中添加
# 创建sku时,需要接收规格数据,删除read_only=True参数
specs = SkuSpecRelatedSerializer(many=True)
class Meta:
model = SKU
exclude = ['create_time', 'update_time']
def create(self, validated_data):
# SKU对应的表中没有属性specs,所以需要从字典中删除
specs = validated_data.pop('specs')
with transaction.atomic(): # 禁止自动提交
sid = transaction.savepoint() # 开启事务
try:
# 创建sku对象
instance = SKU.objects.create(**validated_data)
# 遍历,创建sku规格对象
for item in specs:
spec_id = item.get('spec_id') # 规格编号
option_id = item.get('option_id') # 选项编号
SKUSpecification.objects.create(sku_id=instance.id, spec_id=spec_id, option_id=option_id)
except:
transaction.savepoint_rollback(sid) # 回滚事务
raise serializers.ValidationError('数据保存失败')
else:
transaction.savepoint_commit(sid) # 提交事务
# 为sku生成静态文件
task_generate.delay(instance.id)
return instance
在celery_tasks的detail创建tasks.py
from celery_tasks.main import app
import os
# 千万注意 --项目的包 在 最下面
from apps.goods import models
from apps.contents.utils import get_categories
from apps.goods.utils import get_breadcrumb
from django.template import loader
from django.conf import settings
from apps.goods.models import SKU
def generate_static_sku_detail_html(sku):
# 1.detai_data
# 查询商品频道分类
categories = get_categories()
# 查询面包屑导航
breadcrumb = get_breadcrumb(sku.category)
# 构建当前商品的规格键
sku_specs = sku.specs.order_by('spec_id')
sku_key = []
for spec in sku_specs:
sku_key.append(spec.option.id)
# 获取当前商品的所有SKU
skus = sku.spu.sku_set.all()
# 构建不同规格参数(选项)的sku字典
spec_sku_map = {}
for s in skus:
# 获取sku的规格参数
s_specs = s.specs.order_by('spec_id')
# 用于形成规格参数-sku字典的键
key = []
for spec in s_specs:
key.append(spec.option.id)
# 向规格参数-sku字典添加记录
spec_sku_map[tuple(key)] = s.id
# 获取当前商品的规格信息
goods_specs = sku.spu.specs.order_by('id')
# 若当前sku的规格信息不完整,则不再继续
if len(sku_key) < len(goods_specs):
return
for index, spec in enumerate(goods_specs):
# 复制当前sku的规格键
key = sku_key[:]
# 该规格的选项
spec_options = spec.options.all()
for option in spec_options:
# 在规格参数sku字典中查找符合当前规格的sku
key[index] = option.id
option.sku_id = spec_sku_map.get(tuple(key))
spec.spec_options = spec_options
# 渲染页面
context = {
'categories': categories,
'breadcrumb': breadcrumb,
'sku': sku,
'specs': goods_specs,
}
# 2.detail_template_file
template_file = loader.get_template('detail.html')
# 3.render
html_text = template_file.render(context)
# 4.写入本地
file_path = os.path.join(settings.STATICFILES_DIRS[0], 'detail/{}.html'.format(sku.id))
with open(file_path, 'w') as f:
f.write(html_text)
@app.task(bind=True, name="task_generate", retry_backoff=10)
def task_generate(self, sku_id):
try:
sku = SKU.objects.get(pk=sku_id)
generate_static_sku_detail_html(sku)
except Exception as e:
raise self.retry(exc=e, max_retries=3)