商城后台项目
商城项目DRF框架编写
- 一: 项目初始化
- 1.1: 创建新的子应用:
- 1.2: 在跨域白名单中增加静态服务器域名:
- 1.3: 将meiduo_mall_admin移动到项目中:
- 二:JWT单点登录:
- 2.1: 安装JWT单点登录的拓展组件:
- 2.2: 使用拓展组件完成JWT登录:
- 三:主页数据统计:
- 3.1:用户总数的统计:
- 3.2: 新增用户统计:
- 3.3:日活跃用户统计:
- 3.4:日下单用户统计:
- 3.5:月增用户统计:
- 3.6:日分类商品访问量统计:
- 四:用户管理:
- 4.1:获取用户列表(分页)
- 五:商品管理:
- 5.1:SKU管理:
- 1: 获取所有的商品:
- 2:因此需要定义两个序列化器:
- 2:新增SKU的三级可选分类:
- 3:新增SKU单一资源
- 4:获取SKU单一资源:
- 5:更新SKU单一资源:
- 6:删除SKU单一资源:
- 7: 异步任务与事务的封装:
- 5.2:SPU管理:
- 1: 获取SPU列表数据:
- 2:新增中SPU的可选品牌:
- 3:新增中SPU的可选分类:
- 4:新建SPU,获取单个SPU,更新SPU,删除SPU :
- 5.3:规格管理:
- 5.4:规格选项管理:
- 5.5:品牌管理:
- 5.6:图片管理:
- 1:获取图片列表数据:
- 六:订单管理:
- 1:获取订单的列表信息:
- 2:获取订单的详细信息:
- 3:修改订单的状态:
- 七:系统管理:
- 7.1:权限管理:
- 1、新建编辑`apps/meiduo_admin/serializers/perm_serialziers.py`
- 2、新建编辑`apps/meiduo_admin/views/perm_views.py`
- 3、新建编辑`apps/meiduo_admin/urls.py`
- 7.2:用户组管理:
- 1、新建编辑`apps/meiduo_admin/serializers/group_serialziers.py`
- 2、新建编辑`apps/meiduo_admin/views/goup_views.py`
- 3、新建编辑`apps/meiduo_admin/urls.py`
- 7.3:管理员管理:
- 1、新建编辑`apps/meiduo_admin/serializers/admin_serialziers.py`
- 2、新建编辑`apps/meiduo_admin/views/admin_views.py`
- 3、新建编辑`apps/meiduo_admin/urls.py`
- 八:频道管理:
- 1:序列化返回所有的频道信息:
- 2: 获取单一频道信息:
- 3:创建频道
- 3.1: 获取所有的频道分组:
- 3.2:获取所有频道的一级分类:
- 3.3:创建频道:
- 4:更新频道
- 5:删除频道:
- 九:品牌管理:
- 1:获取品牌列表:
- 2:新建单一品牌数据:
- 3:获取单一品牌数据,更新单一品牌数据,删除单一品牌数据:
一: 项目初始化
1.1: 创建新的子应用:
- 执行命令:python3 …/manage.py startapp meiduo_admin
修改配置文件:
INSTALL_APPS = [
# ...
'apps.meiduo_admin'
]
子应用下创建urls.py
urlpatterns = []
总路由中注册子路由:
urlpatterns = [
# ....
re_path(r'^meiduo_admin/', include('apps.meiduo_admin.urls')),
]
1.2: 在跨域白名单中增加静态服务器域名:
修改dev.py:
# CORS跨域请求白名单设置
CORS_ORIGIN_WHITELIST = (
'http://127.0.0.1:8080',
'http://localhost:8080',
'http://www.meiduo.site:8080',
# 管理站点跨域请求的源
'http://127.0.0.1:8081',
'http://localhost:8081',
'http://www.meiduo.site:8081'
)
1.3: 将meiduo_mall_admin移动到项目中:
- 进入meiduo_admin/dist
- 执行命令: python3 -m http.server 8081 启动项目。
二:JWT单点登录:
2.1: 安装JWT单点登录的拓展组件:
- pip install djangorestframework-jwt
配置文件中配置拓展组件:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 追加Token认证后端 —— 用于验证token有效期识别用户身份
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
JWT_AUTH = {
# 有效期设置为10天
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=10),
}
2.2: 使用拓展组件完成JWT登录:
拓展组件已经帮我们写好了obtain_jwt_token,作为JWT登录的视图,所以需要直接路由映射:
from django.urls import re_path
# obtain_jwt_token为拓展插件提供的用于验证用户名和密码并签发token的视图
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
re_path(r'^authorizations/$', obtain_jwt_token),
]
但是这个拓展组件返回的是他自己写的内容,我们需要指定视图的返回内容怎么办?
- 我们需要在工具类中定义一个函数,这个函数需要返回我们拓展组件指定的返回格式,然后在配置文件中指定就可以了。
- 新建:
meiduo_mall/utils/jwt_response_handlers.py
模块 def jwt_response_payload_handler(token, user=None, request=None):
return {
# 补充返回username和user_id字段
'username': user.username,
'user_id': user.id,
'token': token
}
JWT_AUTH = {
# 设置签发的token的有效期
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=100),
# 来指定拓展插件默认视图返回的响应参数构造函数
'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_mall.utils.jwt_response_handlers.jwt_response_payload_handler'
}
权限限定:要求,不是管理员的不能进入后台管理界面。
"""
思路: 无论是前台页面登录还是后台页面登录都需要经过Django后台,而后台肯定是调用authenticate函数实现用户名和密码的验证, 对于前台我们调用authenticate 传入了request对象,所以我们自定义认证后端得到的request对象是一个对象,而后端登录,我们使用的是第三方拓展组件,这个拓展组件在调用authenticate函数的时候直接传入的是用户名和密码,没有传入request对象,所以在自定义认证后端,如果request对象是None,即是后端登录,并且登录的时候User对象is_staff是False,即不是管理员身份,则认证后端不进行认证了,直接返回None.
"""
# 继承Django默认的传统认证后端
class UsernameMobileAuthBackend(ModelBackend):
# 重写authenticate方法
# 原因:默认的authenticate方法,只会根据username字段去过滤查找用户
def authenticate(self, request, username=None, password=None, **kwargs):
# 允许多账号登陆的情况下,前端传来的"username"有可能是用户名也有可能是手机号
try:
# 1、先按用户名查找
user = User.objects.get(
# username=="18588269037" or mobile=="18588269037"
Q(username=username) | Q(mobile=username) | Q(email=username)
)
except User.DoesNotExist as e:
return None # 用户名找不到,返回None表示认证失败
# TODO:判断是否为管理站点页面登陆,如果是需要进一步校验is_staff=True
# 如果是商城页面登陆,request是一个请求对象
# 如果是管理站点页面登陆,request是一个None
if request is None and not user.is_staff:
return None
# 3、某一个找到了,再校验密码
if user.check_password(password):
return user
三:主页数据统计:
3.1:用户总数的统计:
1: 配置文件修改时区:
LANGUAGE_CODE = 'zh-hans' #中文支持,django1.8以后支持;1.8以前是zh-cn
TIME_ZONE = 'Asia/Shanghai
2: 编辑
apps/meiduo_admin/views/home_views.py
"""
主页接口视图
"""
from django.utils import timezone # Django提供用于处理时间的一个模块
from rest_framework.views import APIView
from rest_framework.response import Response
from apps.users.models import User
# 1、用户总数
class UserTotalCountView(APIView):
def get(self, request):
# 1、提取参数
# 2、校验参数
# 3、数据处理 —— 统计用户总数量
count = User.objects.count()
# 4、构建响应
# cur_date = datetime.now() # 系统本地时间
cur_date = timezone.localtime() # 获取配置参数TIME_ZONE指定时区的时间;返回datetime对象
return Response({
'count': count,
'date': cur_date.date() # 年月日; datetime().date() --> 把datetime类型转化为date类型(只取年月日)
})
2: 路由:
from django.urls import re_path
# obtain_jwt_token为拓展插件提供的用于验证用户名和密码并签发token的视图
from rest_framework_jwt.views import obtain_jwt_token
from meiduo_admin.views.home_views import UserTotalCountView
urlpatterns = [
# 1: 身份认证
re_path(r'^authorizations/$', obtain_jwt_token),
# 2: 用户总数统计
re_path(r'^statistical/total_count/$', UserTotalCountView.as_view()),
]
3.2: 新增用户统计:
配置文件检查配置:
USE_TZ = True
视图:
# 2、日增用户数量统计
class UserDayCountView(APIView):
def get(self, request):
# 当日新增用户数量统计:过滤出"当日"新建的用户数量 date_joined>=当日的零时刻
# 思考:如何获取"当日"的零时刻?!
cur_time = timezone.localtime() # datetime(2020, 9, 26, 17, 8, 56, 612345, tz=<Asia/Shanghai>)
# replace函数把原datetime对象的年月日是分秒微妙和时区属性替换,返回替换后的一个新的datetime对象
cur_0_time = cur_time.replace(hour=0, minute=0, second=0, microsecond=0)
# 这里过滤的时候,Django会全部将时间转换成UTC时间去比较,无需自己手动转换。
count = User.objects.filter(
date_joined__gte=cur_0_time
).count()
return Response({
'count': count,
'date': cur_time.date()
})
路由:
urlpatterns = [
# 1: 身份认证
re_path(r'^authorizations/$', obtain_jwt_token),
# 2: 用户总数统计
re_path(r'^statistical/total_count/$', UserTotalCountView.as_view()),
# 3:统计当日新增用户
re_path(r'^statistical/day_increment/$', UserDayCountView.as_view()),
]
3.3:日活跃用户统计:
视图:
# 3: 日活跃用户统计
class UserActiveCount(APIView):
def get(self, request):
"""
思路: 获取当日零时区的时间,然后统计最后一次登陆时间大于这个时间的人数的数量
:param request:
"""
# 1:获取当日零时刻的时间
cur_0_time = timezone.localtime().replace(hour=0, minute=0,second=0)
# 2: 统计最后登陆时间大于这个时间的人数
count = User.objects.filter(last_login__gte=cur_0_time).count()
# 3: 构建响应返回数据
return Response({
'count': count, 'date': cur_0_time.date()})
路由:
urlpatterns = [
# 4:统计日活跃用户的数量
re_path(r'^statistical/day_active/$', UserActiveCount.as_view()),
]
修改登录后端:
class LoginBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(username=username)|Q(mobile=username))
except Exception as e:
return None
if request is None and not user.is_staff:
return None
if user.check_password(password):
# 登陆的时候修改登陆的时间
user.last_login = timezone.localtime()
user.save()
return user
3.4:日下单用户统计:
1: 视图:
# 日下单用户统计
class UserOrderCountView(APIView):
def get(self, request):
"""
统计当日下单的用户的数量:
思路: 先查询出下的所有的订单, 然后再在订单中筛选出用户来放入一个集合中,最后统计集合的数量
:param request:
"""
# 1: 查询出今天所有的订单
cur_day = timezone.localtime().replace(hour=0, minute=0, second=0)
orders = OrderInfo.objects.filter(create_time__gte=cur_day)
# 2: 根据用户进行去重
my_set = set()
for order in orders:
my_set.add(order.user)
# 3: 统计集合的数量
count = len(my_set)
# 4: 返回响应信息
return Response({
'count': count, 'data': cur_day.date()} )
2: 路由:
# 5: 日下单用户统计
re_path(r'^statistical/day_orders/$', UserOrderCountView.as_view()),
3.5:月增用户统计:
- 需求:统计最近30天的用户,每日的新增用户数量。
路由:
urlpatterns = [
# 6: 月增用户统计
re_path(r'^statistical/month_increment/$', UserMonthCountView.as_view()),
]
视图:
# 月新增用户统计
class UserMonthCountView(APIView):
def get(self, request):
"""
思路:
1:先获取当日零时的时刻,然后再获取29天前的0时刻日期
2:使用列表套字典的形式保存数据
3:循环遍历30次,将每天的次数和日期加入到一个字典中,然后再追加到字典中。
:param request:
"""
# 1: 获取当日的零时
end_0_time = timezone.localtime().replace(hour=0, second=0, minute=0)
# 2: 获取29日之前的零时
start_0_time = end_0_time - timedelta(days=29)
# 3: 准备列表构建返回值
res_list = []
for i in range(30):
# 当日的零时
cur_0_time = start_0_time + timedelta(days=i)
next_0_time = start_0_time + timedelta(days=i+1)
# 统计数量
count = User.objects.filter(date_joined__gte=cur_0_time, date_joined__lt=next_0_time).count()
# 构造字典追加进入列表
res_list.append({
'count': count, 'date': cur_0_time.date()})
# 4:返回列表
return Response(res_list)
3.6:日分类商品访问量统计:
1: 更新商品的模型类:
class GoodsVisitCount(BaseModel):
"""统计分类商品访问量模型类"""
category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='商品分类')
count = models.IntegerField(verbose_name='访问量', default=0)
date = models.DateField(auto_now_add=True, verbose_name='统计日期')
class Meta:
db_table = 'tb_goods_visit'
verbose_name = '统计分类商品访问量'
verbose_name_plural = verbose_name
无需迁移建表
2:在浏览历史记录接口中补充代码实现记录分类商品访问量
# 用户浏览历史记录
class UserBrowseHistory(LoginRequiredJSONMixin, View):
# 添加历史
def post(self, request):
user = request.user
# 1、提取参数
# 2、校验参数
data = json.loads(request.body.decode())
sku_id = data.get('sku_id')
if not sku_id:
return JsonResponse({
'code': 400,
'errmsg': '缺少参数'
}, status=400)
try:
sku = SKU.objects.get(pk=sku_id, is_launched=True)
except SKU.DoesNotExist as e:
return JsonResponse({
'code': 404,
'errmsg': '商品已下架/不存在'
}, status=404)
# 3、数据/业务处理 —— 把访问的sku的id写入redis表示记录一条浏览历史
# 3.1、获取"history"缓存配置的redis链接
conn = get_redis_connection('history')
p = conn.pipeline()
# 3.2、历史记录写入缓存
# 3.2.1、去重
p.lrem(
'history_%d' % user.id,
0, # 删除所有指定成员
sku_id
)
# 3.2.2、插入列表头
p.lpush(
'history_%d' % user.id,
sku_id
)
# 3.2.3、截断保留5个记录
p.ltrim(
'history_%d' % user.id,
0,
4
)
p.execute() # 批量执行redis指令
# TODO: 记录该sku商品的分类访问量
# 分类id:sku.category_id
# 当日零时刻:
cur_0_time = timezone.localtime().replace(hour=0, minute=0, second=0)
# (1)、判断当前sku商品的分类,和当日的数据存不存在;
try:
visit_obj = GoodsVisitCount.objects.get(
category_id=sku.category_id,
create_time__gte=cur_0_time
)
except GoodsVisitCount.DoesNotExist as e:
# 记录不存在则新建
GoodsVisitCount.objects.create(
category_id=sku.category_id,
count=1
)
else:
# 记录存在则累加
visit_obj.count += 1
visit_obj.save()
# 4、构建响应
return JsonResponse({
'code': 0,
'errmsg': 'ok'
})
3: 序列化器:
apps/meiduo_admin/serializers/home_serializers.py
"""
主页接口序列化器
"""
from rest_framework import serializers
from apps.goods.models import GoodsVisitCount
# 定义一个序列还器,用于序列化GoodsVisitCount模型类数据
class GoodsVisitModelSerializer(serializers.ModelSerializer):
# category = serializers.PrimaryKeyRelatedField() --> 序列化的结果是关联分类对象的主键值
category = serializers.StringRelatedField()
class Meta:
model = GoodsVisitCount
fields = ['category', 'count']
4:视图:编辑
apps/meiduo_admin/views/home_views.py
from ..serializers.home_serializers import *
# 6、获取分类访问量列表数据
class GoodsDayView(ListAPIView):
# 如果在类属性中获取0时刻,这个cur_0_time记录的永远都是服务器启动的那一天的0时刻了;
# cur_0_time = timezone.localtime().replace(hour=0, minute=0, second=0)
# queryset = GoodsVisitCount.objects.filter(
# create_time__gte=cur_0_time
# )
queryset = GoodsVisitCount.objects.all()
serializer_class = GoodsVisitModelSerializer
# 在每一次请求的时候,都是通过get_queryset方法来获取查询集
def get_queryset(self):
cur_0_time = timezone.localtime().replace(hour=0, minute=0, second=0)
return self.queryset.filter(
create_time__gte=cur_0_time
)
5:路由:
urlpatterns = [
# ......
# 6、日分类商品访问量统计
re_path(r'^statistical/goods_day_views/$', GoodsDayView.as_view()),
]
四:用户管理:
4.1:获取用户列表(分页)
1:定义一个用户的序列化器:
from rest_framework import serializers
from apps.users.models import User
# 1:定义用户的序列化器:使用ModelSerializer模型类序列化器
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
# 1: 指定模型类
model = User
# 2: 制定哪些需要映射
fields = [
'id',
'username',
'mobile',
'email'
]
2:自定义分页器:
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class MyPage(PageNumberPagination):
page_query_param = 'page' # ?page=1
page_size_query_param = 'pagesize' # ?pagesize=5
max_page_size = 10
page_size = 5
def get_paginated_response(self, data):
return Response({
'counts': self.page.paginator.count, # 总数量
'lists': data, # 查询集分页的子集(当前页数据)
'page': self.page.number, # 当前页
'pages': self.page.paginator.num_pages, # 总页数
'pagesize': self.page_size
})
3:定义视图:
from rest_framework.generics import ListAPIView
from apps.meiduo_admin.paginations import MyPage
from apps.meiduo_admin.serializers.user_serializers import UserModelSerializer
from apps.users.models import User
class UserView(ListAPIView):
queryset = User.objects.filter(is_staff=True).order_by('id')
serializer_class = UserModelSerializer
pagination_class = MyPage
def get_queryset(self):
# 根据查询字符串参数keyword过滤
# 1、提取keyword
# 问题:如何在非视图函数中获取请求对象?!
# 答:在django/drf中,每次请求的时候,框架除了把请求对象request传入视图函数以外
# 还会把请求对象封装到self.request中(self指的是视图对象)
keyword = self.request.query_params.get('keyword')
# 2、根据keyword过滤
if keyword:
return self.queryset.filter(username__contains=keyword)
return self.queryset.all() # 获取最新数据
4:路由:
# 8: 用户的列表查询
re_path(r"^users/$", UserView.as_view()),
4.2:新增用户(管理员):
问题: 我们在新建用户对象的时候,
ModelSerializer
默认提供的create
方法无法帮助我们进行:- (1)密码加密;
- (2)设置is_staff=True
解决方案?
- 我们重写序列化器的
create
方法,来实现密码加密和添加is_staff=True
! - 我们还可以在校验过程中,对有效数据中的明文密码加密,以及在返回的有效数据中添加
is_staff=True
—— 后续create
方法完成新建对象就是使用调整之后的有效数据!
- 我们重写序列化器的
- 方案一: 重写序列化器的create方法实现密码加密和添加属性。
修改序列化器:
```python
from rest_framework import serializers
from apps.users.models import User
# 1:定义用户的序列化器:使用ModelSerializer模型类序列化器
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
# 1: 指定模型类
model = User
# 2: 制定哪些需要映射
fields = [
'id',
'username',
'mobile',
'email'
]
# 重写create方法实现加密和设置属性:
def create(self, validated_data):
# 1: 设置is_staff 属性是True
validated_data['is_staff'] = True
# 2: 密码加密
user = User.objects.create_user(**validated_data)
# 3: 返回
return user
视图增加继承: CreateAPIView,路由保持不变。
方案二:校验的过程中进行加密
# 1:定义用户的序列化器:使用ModelSerializer模型类序列化器
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
# 1: 指定模型类
model = User
# 2: 制定哪些需要映射
fields = [
'id',
'username',
'mobile',
'email'
]
def validate(self, attrs):
# 1、密码加密
raw_password = attrs.pop('password') # 明文
secret_password = make_password(raw_password) # make_password传入明文密码,加密返回密文码
attrs['password'] = secret_password
# 2、返回有效数据中添加is_staff=True
attrs['is_staff'] = True
return attrs
五:商品管理:
5.1:SKU管理:
1: 获取所有的商品:
1: 序列化器如何定义?
通过分析得知: 外键字段,外键的主键字段,主表的隐藏字段都不会自动映射,需要手动映射。
2:因此需要定义两个序列化器:
from rest_framework import serializers
from apps.goods.models import SKUSpecification, SKU
class SKUSpecOptModelSerializer(serializers.ModelSerializer):
# 因为这2个字段必须参与反序列化,所有我们只能手动映射覆盖原有的默认映射(默认只作用于序列化)
spec_id = serializers.IntegerField()
option_id = serializers.IntegerField()
class Meta:
model = SKUSpecification
# 如果关联对象主键隐藏字段,我们在fields中显示声明,则会自动映射类型为ReadOnlyField —— 只读(只参与序列化)
fields = [
'spec_id',
'option_id'
]
class SKUModelSerializer(serializers.ModelSerializer):
# 三个隐藏字段:
# 1、本表主键id,会自动映射
# 2、外间关联对象的主键隐藏字段(如:spu_id), 不会自动映射
spu = serializers.StringRelatedField()
spu_id = serializers.IntegerField()
category = serializers.StringRelatedField()
category_id = serializers.IntegerField()
# 3、从表related_name指定的主表隐藏字段,不会自动映射
# 表示当前SKU对象(主表)关联的"多个"SKUSpecification对象(从表)
specs = SKUSpecOptModelSerializer(many=True)
class Meta:
model = SKU
fields = "__all__"
3:定义路由:
re_path(r'^skus/$', SKUGoodsView.as_view({
'get': 'list'})),
4:定义视图:
from rest_framework.viewsets import ModelViewSet
from apps.goods.models import SKU
from apps.meiduo_admin.paginations import MyPage
from apps.meiduo_admin.serializers.sku_serializers import SKUModelSerializer
class SKUGoodsView(ModelViewSet):
queryset = SKU.objects.all()
serializer_class = SKUModelSerializer
pagination_class = MyPage
# 进行过滤
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(name__contains=keyword)
return self.queryset.all()
2:新增SKU的三级可选分类:
1:序列化器怎么写?接口定义: 只需要返回SKU的名字和id
# SKU三级分类的序列化器:
class SKUCategorySimpleSerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory # 商品分类表
fields = [
'id',
'name'
]
2:路由:
# 10: SKU商品的三级分类
re_path(r'^skus/categories/$', SKUCategoryView.as_view()),
3:视图: 选择哪一个?ListAPIView
# 返回SKU商品分类的三级分类
class SKUCategoryView(ListAPIView):
queryset = GoodsCategory.objects.all()
serializer_class = SKUCategorySimpleSerializer
def get_queryset(self):
# 过滤三级分类
return self.queryset.filter(parent_id__gte=37)
3: 获取SPU商品的表名称数据:
1: 创建SPU的序列化器:
# SPU的序列化器:
class SPUSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SPU
fields = [
'id',
'name'
]
2:路由:
# 11: SPU的路由:
re_path(r'^goods/simple/$', SPUSimpleView.as_view()),
3:视图:
# 获取SPU的信息
class SPUSimpleView(ListAPIView):
queryset = SPU.objects.all()
serializer_class = SPUSimpleSerializer
SKU的可选规格和选项:
1: 序列化器分析:
分析可以得出,需要对SPUSpecification生成序列化器,但是要同时序列化SKUSpecification。
2:编写序列化器:
class SpecOptSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SpecificationOption
fields = [
'id',
'value'
]
class SPUSpecModelSerializer(serializers.ModelSerializer):
spu = serializers.StringRelatedField()
spu_id = serializers.IntegerField()
# 关联从表SpecficationOption多条数据
options = SpecOptSimpleSerializer(many=True)
class Meta:
model = SPUSpecification
fields = [
'id',
'name',
'spu',
'spu_id',
'options'
]
3: 路由:
# 12: sku的可选规格和选项
re_path(r'^goods/(?P<pk>\d+)/specs/$', SPUSpecView.as_view()),
4:视图:
分析: 首先是序列化返回多个,选择ListAPIView, 而需要根据路径中的spu_id过滤出关联的多个规格数据,因此需要重写get_queryset方法进行过滤。
class SPUSpecView(ListAPIView):
queryset = SPUSpecification.objects.all()
serializer_class = SPUSpecModelSerializer
def get_queryset(self):
# 根据路径中的spu_id过滤出其关联的多个规格数据
# 命名分组正则提取出来的参数,封装在self.kwargs中(是一个字典,分组名作为key,提取的传值作为value)
# 非命名分组正则提取出来的参数,封装在self.args中(是一个列表,按照分组顺序提取参数)
spu_id = self.kwargs.get('pk')
return self.queryset.filter(spu_id=spu_id)
3:新增SKU单一资源
1:分析序列化器如何构建?
分析表结构可以得到, spu和category是外键关联字段,需要手动映射,specs是主表隐藏字段,需要关联反序列化。
2:构建序列化器:由于我们之前有了这两个序列化器,我们看看原来的序列化器能不能完成任务。
在上图我们可以发现在进行反序列化的时候,我的SKU序列化器不能完成新建SKU规格和选项的操作。怎么办呢?需要重写SKU序列化器的create方法才可以。
思路: 重写create方法然后,将校验的数据分成SKU的和SKU规格数据的两部分。使用SKU的 创建SKU对象,然后循环遍历SKU规格和选项,将SKU规格和选项中再增加上SKU的ID,然后创建规格和选项就可以了,注意最后一定要返回sku。
# 重写模型类序列化器的create方法的原因
# 默认create方法,无法帮助我们插入中间表数据来记录新增sku商品的规格和选项信息
def create(self, validated_data):
# [{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}]
specs = validated_data.pop('specs')
# 1、新建sku模型类对象(主表)
sku = SKU.objects.create(**validated_data)
# 2、新建规格选项中间表数据,来记录新增sku的规格和选项信息
for temp in specs:
# temp : {spec_id: "4", option_id: 8}
temp['sku_id'] = sku.id
SKUSpecification.objects.create(**temp)
return sku
3:新增SKU路由:
re_path(r'^skus/$', SKUGoodsView.as_view({
'get': 'list', 'post': 'create'})),
- 4:视图:我们发现我们的视图不用改变。
4:获取SKU单一资源:
1:路由:
re_path(r'^skus/(?P<pk>\d+)/$', SKUGoodsView.as_view({
'get': 'retrieve'
})),
2:视图,以前的还能用。
- 3: 序列化器: 无需修改。
5:更新SKU单一资源:
1: 路由:
re_path(r'^skus/(?P<pk>\d+)/$', SKUGoodsView.as_view({
'put': 'update'
})),
- 2:视图:无需修改
3:序列化器:
我们的序列化器, 不能完成更新操作,因此需要重写update方法。
# 默认update方法,无法完成中间表数据的更新
def update(self, instance, validated_data):
# [{spec_id: "1", option_id: 1}, {spec_id: "2", option_id: 4}, {spec_id: "3", option_id: 6}]
specs = validated_data.pop('specs')
# TODO: 两张表动作必须保证事务特性
# 0、更新主表数据
sku = super().update(instance, validated_data)
# 1、删除原有中间表数据
SKUSpecification.objects.filter(
sku_id=sku.id
).delete()
# 2、插入新的规格和选项中间表数据
for temp in specs:
# temp: {spec_id: "1", option_id: 1}
temp['sku_id'] = sku.id
SKUSpecification.objects.create(**temp)
# TODO: 重新静态化生成新的详情页
return sku
6:删除SKU单一资源:
1:路由:
re_path(r'^skus/(?P<pk>\d+)/$', SKUGoodsView.as_view({
'delete': 'destroy'
})),
- 2:视图:无需修改
- 3:序列化器:无需修改
7: 异步任务与事务的封装:
需求:一旦我们增加了某个SKU,则我们的前台页面肯定要增加一个SKU商品的页面。
如何去做呢?思路: 采用异步的方式,静态化的生成当前SKU商品的详情页。
增加异步方式生成页面,另外设置默认的图片。
class SKUModelSerializer(serializers.ModelSerializer):
# 三个隐藏字段:
# 1、本表主键id,会自动映射
# 2、外间关联对象的主键隐藏字段(如:spu_id), 不会自动映射
spu = serializers.StringRelatedField()
spu_id = serializers.IntegerField()
category = serializers.StringRelatedField()
category_id = serializers.IntegerField()
# 3、从表related_name指定的主表隐藏字段,不会自动映射
# 表示当前SKU对象(主表)关联的"多个"SKUSpecification对象(从表)
specs = SKUSpecOptModelSerializer(many=True)
class Meta:
model = SKU
fields = "__all__"
# 默认create方法,无法帮助我们插入中间表数据来记录新增sku商品的规格和选项信息
def create(self, validated_data):
# [{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}]
specs = validated_data.pop('specs')
# 设计默认图片
validated_data['default_image'] = 'group1/M00/00/02/CtM3BVrPB4GAWkTlAAGuN6wB9fU4220429'
# TODO: 下面两步操作,是数据库两张表的插入动作,必须保证"事务"特性
with transaction.atomic():
save_id = transaction.savepoint()
try:
# 1、新建sku模型类对象(主表)
sku = SKU.objects.create(**validated_data)
# 2、新建规格选项中间表数据,来记录新增sku的规格和选项信息
for temp in specs:
# temp : {spec_id: "4", option_id: 8}
temp['sku_id'] = sku.id
SKUSpecification.objects.create(**temp)
except Exception as e:
transaction.savepoint_rollback(save_id)
raise serializers.ValidationError('数据库新建失败!')
transaction.savepoint_commit(save_id)
# TODO: 使用异步任务方式,静态化生成当前新建sku商品的详情页
generate_static_sku_detail_html.delay(sku.id)
return sku
新建文件
celery_tasks/html/tasks.py
,编辑如下:import os
from django.template import loader
from django.conf import settings
from apps.goods.utils import get_categories,get_goods_and_spec
from celery_tasks.main import celery_app
@celery_app.task(name='generate_static_sku_detail_html')
def generate_static_sku_detail_html(sku_id):
"""
功能:生成指定sku商品的详情页面
sku_id: SKU商品的id
无
"""
categories = get_categories()
goods, sku, specs = get_goods_and_spec(sku_id)
# =========模版渲染========
template = loader.get_template('detail.html')
context = {
'categories': categories,
'goods': goods, # 当前sku从属的spu
'specs': specs, # 规格和选项信息
'sku': sku # 当前sku商品对象
}
page = template.render(context)
# settings.STATIC_FILE_PATH --> 是front_end_pc文件夹路径
file_path = os.path.join(
settings.STATIC_FILE_PATH,
'goods/%d.html' % sku_id, # 'goods/1.html'
)
with open(file_path, 'w') as f:
f.write(page)
5.2:SPU管理:
1: 获取SPU列表数据:
1: 路由:
# 15: SPU获取列表数据:
re_path(r'^goods/$', SPUView.as_view({
'get': 'list'})),
2:序列化器如何写?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUFdd3KO-1616158242025)(C:\Users\11737\AppData\Roaming\Typora\typora-user-images\1616073711080.png)]
3:序列化器的代码编写:
from apps.goods.models import SPU
class SPUModelSerializer(serializers.ModelSerializer):
brand = serializers.StringRelatedField()
brand_id = serializers.IntegerField()
category1_id = serializers.IntegerField()
category2_id = serializers.IntegerField()
category3_id = serializers.IntegerField()
class Meta:
model = SPU
fields = [
'id',
'name',
'brand',
'brand_id',
'category1_id',
'category2_id',
'category3_id',
'sales',
'comments',
'desc_detail',
'desc_pack',
'desc_service'
]
4: 视图:
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ModelViewSet
from ..serializers.spu_serializers import *
from ..paginations import MyPage
class SPUView(ModelViewSet):
queryset = SPU.objects.all().order_by('id')
serializer_class = SPUModelSerializer
pagination_class = MyPage
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(name__contains=keyword)
return self.queryset.all()
2:新增中SPU的可选品牌:
路由:
re_path(r'^goods/brands/simple/$', BrandSimpleView.as_view()),
序列化器:
class BrandSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = [
'id',
'name'
]
3:视图:
class BrandSimpleView(ListAPIView):
queryset = Brand.objects.all()
serializer_class = BrandSimpleSerializer
3:新增中SPU的可选分类:
1:路由:
# 17: 新增SPU的可选分类
# 新增SPU可选一级分类
re_path(r'^goods/channel/categories/$', SPUCateSimpleView.as_view()),
# 新增SPU可选二级或三级份额里
re_path(r'^goods/channel/categories/(?P<pk>\d+)/$', SPUCateSimpleView.as_view()),
2: 序列化器:
# SPU可选分类的序列化器
class SPUCateSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = ['id', 'name']
3:视图:
class SPUCateSimpleView(ListAPIView):
queryset = GoodsCategory.objects.all()
serializer_class = SPUCateSimpleSerializer
def get_queryset(self):
parent_id = self.kwargs.get('pk')
# 如果是二级、三级分类接口调用,根据路径pk过滤出子集返回
if parent_id:
return self.queryset.filter(parent_id=parent_id)
# 如果是一级分类接口调用
return self.queryset.filter(parent=None)
4:新建SPU,获取单个SPU,更新SPU,删除SPU :
1:路由:
# 15: SPU获取列表数据:
re_path(r'^goods/$', SPUView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^goods/(?P<pk>\d+)/$', SPUView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
- 序列化器和视图发现都无需改变。
5.3:规格管理:
- 1:序列化器:
新建编辑
apps/meiduo_admin/serializers/spec_serializers.py
from rest_framework import serializers
from apps.goods.models import SPUSpecification
class SpecModelSerializer(serializers.ModelSerializer):
spu = serializers.StringRelatedField()
spu_id = serializers.IntegerField()
class Meta:
model = SPUSpecification
fields = [
'id',
'name',
'spu',
'spu_id'
]
2:视图:
新建编辑
apps/meiduo_admin/views/spec_views.py
from rest_framework.viewsets import ModelViewSet
from ..serializers.spec_serializers import *
from ..paginations import MyPage
class SpecView(ModelViewSet):
queryset = SPUSpecification.objects.all()
serializer_class = SpecModelSerializer
pagination_class = MyPage
3:路由:
# 规格管理
re_path(r'^goods/specs/$', SpecView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^goods/specs/(?P<pk>\d+)/$', SpecView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
5.4:规格选项管理:
- 序列化器:
新建编辑
apps/meiduo_admin/serializers/spec_serializers.py
from rest_framework import serializers
from apps.goods.models import SpecificationOption,SPUSpecificationclass OptSpecSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SPUSpecification
fields = [
'id',
'name'
]
class OptionModelSerializer(serializers.ModelSerializer):
spec = serializers.StringRelatedField()
spec_id = serializers.IntegerField()
class Meta:
model = SpecificationOption
fields = [
'id',
'value',
'spec',
'spec_id'
]
- 视图:
新建编辑
apps/meiduo_admin/views/spec_views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import ListAPIView
from ..serializers.option_serializers import *
from ..paginations import MyPage
class OptSpecSimpleView(ListAPIView):
queryset = SPUSpecification.objects.all()
serializer_class = OptSpecSimpleSerializer
class OptionView(ModelViewSet):
queryset = SpecificationOption.objects.all()
serializer_class = OptionModelSerializer
pagination_class = MyPage
- 路由:
编辑
apps/meiduo_admin/urls.py
urlpatterns = [
# ......
# 选项管理
re_path(r'^specs/options/$', OptionView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^specs/options/(?P<pk>\d+)/$', OptionView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
# 新增选项可选规格
re_path(r'^goods/specs/simple/$', OptSpecSimpleView.as_view()),
]
5.5:品牌管理:
5.6:图片管理:
1:获取图片列表数据:
1: 定义序列化器:
from rest_framework import serializers
from apps.goods.models import SKUImage, SKU
from fdfs_client.client import Fdfs_client
class SKUSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SKU
fields = [
'id',
'name'
]
class ImageModelSerializer(serializers.ModelSerializer):
class Meta:
model = SKUImage
fields = [
'id',
'sku',
'image'
]
def validate(self, attrs):
# sku类型是PrimaryKeyRelatedField;反序列化前端传来主键值,经过类型校验之后成了对应的对象
# image字段类型是ImageField;发序列化前端传来文件数据,经过类型校验之后成了文件对象
# 规则1:SKUImage(sku=<SKU对象>, image=<文件对象>) ---> image赋值为一个文件对象的话,会触发文件存储后端来完成;
# 规则2:SKUImage(sku=<SKU对象>, image="图片标示,图片id") ---> image赋值为一个字符串,那么就不会触发文件存储后端,直接将该字符串存入mysql;
# 手动实现上传图片
image_obj = attrs.get('image') # 图片文件对象
# (1)、在校验过程中,手动从文件对象中读取图片数据,上传fdfs
conn = Fdfs_client('./meiduo_mall/settings/client.conf')
res = conn.upload_by_buffer(image_obj.read())
if res is None:
raise serializers.ValidationError("fdfs上传失败!")
# (2)、再把fdfs返回的文件标示(文件id)作为有效数据中image字段的值
attrs['image'] = res['Remote file_id']
return attrs
2: 定义图片的视图:
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ModelViewSet
from goods.models import SKU, SKUImage
from meiduo_admin.paginations import MyPage
from meiduo_admin.serializers.image_serialisers import SKUSimpleSerializer, ImageModelSerializer
class SKUSimpleView(ListAPIView):
queryset = SKU.objects.all()
serializer_class = SKUSimpleSerializer
class ImageView(ModelViewSet):
queryset = SKUImage.objects.all()
serializer_class = ImageModelSerializer
pagination_class = MyPage
3:修改FSATDFS文件存储后端:
把
client.conf
配置文件放入settings
目录中;# Storage是默认的Django的存储后端
from django.core.files.storage import Storage
from django.conf import settings
from fdfs_client.client import Fdfs_client
from rest_framework import serializers
class FastDFSStorage(Storage):
def _open(self, name, mode='rb'):
# 打开django本地文件
pass
def _save(self, name, image_obj, max_length=None):
# 如果SKUImage(image=<文件对象>) --> 当前存储后端的_save方法,完成图片的保存动作
# 此处我们需要把文件保存到fdfs(上传到fdfs)
# name: 文件名称,同时也是保存到Django本地的文件名称 ---> 无需使用
# image_obj: 传来的文件对象 --> 就是ImageField字段在新建或者更新的时候被赋值的文件对象
conn = Fdfs_client('./meiduo_mall/settings/client.conf')
res = conn.upload_by_buffer(image_obj.read())
if res is None:
raise serializers.ValidationError('上传fdfs失败!')
file_id = res['Remote file_id']
# 注意:_save方法返回值就是当前字段,存储在mysql中的文件id
return file_id
def exists(self, name):
# 功能:判断上传的文件在Django本地是否重复
# True:表示重复
# Fales:表示不重复
return False
def url(self, name):
"""
功能:返回值就是ImageField.url属性 ---> 构建完整的图片(文件)链接
:param name: ImageField类型字段在mysql中存储的值 ---> 文件索引标识"group1/M00/00/02/CtM3BVrPB5CALKn6AADq-Afr0eE1672090"
图片链接
"""
# return "group1/M00/00/02/CtM3BVrPB5CALKn6AADq-Afr0eE1672090"
# return name
# return "http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrPB5CALKn6AADq-Afr0eE1672090"
# return "http://image.meiduo.site:8888/" + name
return settings.FDFS_BASE_URL + name
4:路由:
# 20:图片管理
re_path(r"^skus/images/$", ImageView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^specs/options/(?P<pk>\d+)/$', ImageView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
六:订单管理:
1:获取订单的列表信息:
1:路由:
urlpatterns = [
# ......
# 订单列表
re_path(r'^orders/$', OrderView.as_view({
'get':'list'})),
]
2:序列化器:
from rest_framework import serializers
from apps.orders.models import OrderInfo,OrderGoods
from apps.goods.models import SKU
class OrderSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = OrderInfo
fields = [
'order_id',
'create_time'
]
3:视图:
from meiduo_admin.paginations import MyPage
from meiduo_admin.serializers.orders_serializers import OrderSimpleSerializer
from apps.orders.models import OrderInfo
class OrderView(UpdateModelMixin, ReadOnlyModelViewSet):
queryset = OrderInfo.objects.all().order_by('create_time')
serializer_class = OrderSimpleSerializer
pagination_class = MyPage
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(order_id__contains=keyword)
return self.queryset.all()
2:获取订单的详细信息:
1:路由:
re_path(r'^orders/(?P<pk>\d+)/$', OrderView.as_view({
'get':'retrieve'})),
2:序列化器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwfHGUBM-1616158242026)(C:\Users\11737\AppData\Roaming\Typora\typora-user-images\1616138743367.png)]
class SKUSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SKU
fields = [
'name',
'default_image'
]
class OrderGoodsModelSerializer(serializers.ModelSerializer):
# 当前订单商品对象关联的"单一的"SKU对象
sku = SKUSimpleSerializer()
class Meta:
model = OrderGoods
fields = [
'count',
'price',
'sku'
]
class OrderDetailSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField()
# 当前订单对象关联的多个订单商品(OrderGoods)对象
skus = OrderGoodsModelSerializer(many=True)
class Meta:
model = OrderInfo
fields = "__all__"
3:视图:对于同一个视图,我们如何指定不同的方式用不同的序列化器呢?
重写get_serializer_class方法。
class OrderView(UpdateModelMixin, ReadOnlyModelViewSet):
queryset = OrderInfo.objects.all().order_by('create_time')
serializer_class = OrderSimpleSerializer
pagination_class = MyPage
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(order_id__contains=keyword)
return self.queryset.all()
def get_serializer_class(self):
# 功能:获取操作数据使用序列化器类;默认返回类属性serializer_class
# 区分2个逻辑
# (1)、如果调用的是获取列表数据接口self.list, 使用OrderSimpleSerializer
# (2)、如果调用的是获取单一详情数据接口self.retrieve, 使用OrderDetailSerializer
# 知识点:self.action属性就是当前请求的视图函数的名称!
if self.action == "list":
return OrderSimpleSerializer
elif self.action == "retrieve":
return OrderDetailSerializer
return self.get_serializer_class
3:修改订单的状态:
路由:
# 23: 修改订单的状态
re_path(r'^orders/(?P<pk>\d+)/status/$', OrderView.as_view({
'patch': 'partial_update'})),
修改视图:
def get_serializer_class(self):
# 功能:获取操作数据使用序列化器类;默认返回类属性serializer_class
# 区分2个逻辑
# (1)、如果调用的是获取列表数据接口self.list, 使用OrderSimpleSerializer
# (2)、如果调用的是获取单一详情数据接口self.retrieve, 使用OrderDetailSerializer
# 知识点:self.action属性就是当前请求的视图函数的名称!
if self.action == "list":
return OrderSimpleSerializer
elif self.action == "retrieve":
return OrderDetailSerializer
elif self.action == 'partial_update':
return OrderDetailSerializer
return self.get_serializer_class
七:系统管理:
7.1:权限管理:
1、新建编辑apps/meiduo_admin/serializers/perm_serialziers.py
from django.contrib.auth.models import Permission,ContentType
from rest_framework import serializers
class PermContentTypeSerializer(serializers.ModelSerializer):
class Meta:
model = ContentType
fields = [
'id',
'name'
]
class PermModelSerializer(serializers.ModelSerializer):
# content_type = serializers.StringRelatedField()
class Meta:
model = Permission
fields = [
'id',
'name',
'codename',
'content_type'
]
2、新建编辑apps/meiduo_admin/views/perm_views.py
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ModelViewSet
from ..serializers.perm_serializers import *
from ..paginations import MyPage
class PermContentTypeView(ListAPIView):
queryset = ContentType.objects.all()
serializer_class = PermContentTypeSerializer
class PermView(ModelViewSet):
queryset = Permission.objects.all()
serializer_class = PermModelSerializer
pagination_class = MyPage
def get_queryset(self):
return self.queryset.order_by('pk')
3、新建编辑apps/meiduo_admin/urls.py
urlpatterns = [
# ....
# 权限管理
re_path(r'^permission/perms/$', PermView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^permission/perms/(?P<pk>\d+)/$', PermView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
# 新增权限可选类型
re_path(r'^permission/content_types/$', PermContentTypeView.as_view()),
]
7.2:用户组管理:
1、新建编辑apps/meiduo_admin/serializers/group_serialziers.py
from django.contrib.auth.models import Group,Permission
from rest_framework import serializers
class GroupPermSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Permission
fields = [
'id',
'name'
]
class GroupModelSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = [
'id',
'name',
# 假设模型类序列化器可以完成该字段的校验和新建中间表数据;
'permissions'
]
# 模型类序列化器的create方法,会帮助我们根据ManyToManyField字段来构建中间表数据
# 接下来手动使用ManyToManyField字段来构建中间表数据
# def create(self, validated_data):
# permissions = validated_data.pop('permissions')
# # 1、新建主表分组对象
# group = Group.objects.create(**validated_data)
# # 2、根据前端传来的permisssions列表数据新增中间表
# group.permissions.set(permissions)
# # group.permissions = permissions
#
# return group
2、新建编辑apps/meiduo_admin/views/goup_views.py
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ModelViewSet
from ..serializers.group_serializers import *
from ..paginations import MyPage
class GroupPermView(ListAPIView):
queryset = Permission.objects.all()
serializer_class = GroupPermSimpleSerializer
class GroupView(ModelViewSet):
queryset = Group.objects.all()
serializer_class = GroupModelSerializer
pagination_class = MyPage
3、新建编辑apps/meiduo_admin/urls.py
urlpatterns = [
# ....
# 分组管理
re_path(r'^permission/groups/$', GroupView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^permission/groups/(?P<pk>\d+)/$', GroupView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
# 新增分组可选权限
re_path(r'^permission/simple/$', GroupPermView.as_view()),
]
7.3:管理员管理:
1、新建编辑apps/meiduo_admin/serializers/admin_serialziers.py
from django.contrib.auth.models import Group
from django.contrib.auth.hashers import make_password
from rest_framework import serializers
from apps.users.models import User
class AdminGroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = [
'id',
'name'
]
class AdminUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'username',
'email',
'mobile',
'password',
'groups',
'user_permissions'
]
extra_kwargs = {
'password': {
'write_only': True}
}
def validate(self, attrs):
raw_password = attrs.get('password')
secret_password = make_password(raw_password)
attrs['password'] = secret_password
attrs['is_staff'] = True
return attrs
2、新建编辑apps/meiduo_admin/views/admin_views.py
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ModelViewSet
from ..serializers.admin_serializers import *
from ..paginations import MyPage
class AdminGroupView(ListAPIView):
queryset = Group.objects.all()
serializer_class = AdminGroupSerializer
class AdminUserView(ModelViewSet):
queryset = User.objects.filter(is_staff=True)
serializer_class = AdminUserSerializer
pagination_class = MyPage
3、新建编辑apps/meiduo_admin/urls.py
urlpatterns = [
# ....
# 管理员用户
re_path(r'^permission/admins/$', AdminUserView.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'^permission/admins/(?P<pk>\d+)/$', AdminUserView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
# 新增管理员可选权限
re_path(r'^permission/groups/simple/$', AdminGroupView.as_view()),
]
八:频道管理:
1:序列化返回所有的频道信息:
1: 路由:
# 27:序列化返回所有的频道信息
re_path(r'^goods/channels/$', ChannelsView.as_view({
'get': 'list'})),
2:视图:
from rest_framework import serializers
from apps.goods.models import GoodsChannel
# 1:频道的序列化器
class ChannelsSerializer(serializers.ModelSerializer):
group_id = serializers.IntegerField()
category_id = serializers.IntegerField()
class Meta:
model = GoodsChannel
fields = "__all__"
3: 序列化器:
from rest_framework import serializers
from apps.goods.models import GoodsChannel
# 1:频道的序列化器
class ChannelsSerializer(serializers.ModelSerializer):
class Meta:
model = GoodsChannel
fields = "__all__"
2: 获取单一频道信息:
1:路由:
re_path(r'^goods/channels/(?P<pk>\d+)/$', ChannelsView.as_view({
'get': 'retrieve'})),
2:视图:增加过滤
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(order_id__contains=keyword)
return self.queryset.all()
- 3:序列化器无需改变。
3:创建频道
3.1: 获取所有的频道分组:
1: 路由:
# 28: 获取所有的频道组信息
re_path(r"^goods/channel_types/$", Channels_Group_View.as_view({
'get': 'list'})),
2:序列化器:
# 2: 序列化返回频道分组序列化器:
class Channels_Group_Serializer(serializers.ModelSerializer):
class Meta:
model = GoodsChannelGroup
fields = "__all__"
3:视图:
# 2: 序列化返回所有的频道分组列表
class Channels_Group_View(ModelViewSet):
queryset = GoodsChannelGroup.objects.all().order_by('id')
serializer_class = Channels_Group_Serializer
3.2:获取所有频道的一级分类:
1:路由:
29: 获取频道所有一级类目:
re_path(r"^goods/categories/$", Channels_first_View.as_view({
'get': 'list'})),
2:序列化器:
3: 获取频道所有一级类目
class GoodsCategory_serializers(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = "__all__"
3:视图:
3: 获取频道所有一级类目
class Channels_first_View(ModelViewSet):
queryset = GoodsCategory.objects.all().order_by('id').filter(id__lte=37)
serializer_class = GoodsCategory_serializers
3.3:创建频道:
1: 路由:
re_path(r'^goods/channels/$', ChannelsView.as_view({
'get': 'list', 'post': 'create'})),
2: 序列化器:
# 1:频道的序列化器
class ChannelsSerializer(serializers.ModelSerializer):
group = serializers.StringRelatedField()
group_id = serializers.IntegerField()
category = serializers.StringRelatedField()
category_id = serializers.IntegerField()
class Meta:
model = GoodsChannel
fields = "__all__"
3:视图:
# 1: 序列化返回频道列表
class ChannelsView(ModelViewSet):
queryset = GoodsChannel.objects.all().order_by('id')
serializer_class = ChannelsSerializer
pagination_class = MyPage
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(order_id__contains=keyword)
return self.queryset.all()
4:更新频道
1:路由:
re_path(r'^goods/channels/(?P<pk>\d+)/$', ChannelsView.as_view({
'get': 'retrieve', 'put': 'update'})),
- 2: 序列化器和视图不用改变。
5:删除频道:
1: 路由:
re_path(r'^goods/channels/(?P<pk>\d+)/$', ChannelsView.as_view({
'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
- 3:其余不用改变。
九:品牌管理:
1:获取品牌列表:
1:序列化器:
from rest_framework import serializers
from apps.goods.models import Brand
class Brands_Serializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = "__all__"
2:路由:
# 30: 获取品牌列表
re_path(r"^goods/brands/$", Brands_views.as_view({
'get': 'list'})),
3:视图:
from rest_framework.viewsets import ModelViewSet
from apps.goods.models import Brand
from meiduo_admin.paginations import MyPage
from meiduo_admin.serializers.brands_serializers import Brands_Serializer
class Brands_views(ModelViewSet):
queryset = Brand.objects.all().order_by('id')
serializer_class = Brands_Serializer
pagination_class = MyPage
def get_queryset(self):
keyword = self.request.query_params.get('keyword')
if keyword:
return self.queryset.filter(order_id__contains=keyword)
return self.queryset.all()
2:新建单一品牌数据:
1: 路由:
re_path(r"^goods/brands/$", Brands_views.as_view({
'get': 'list', 'post': 'create'})),
3:获取单一品牌数据,更新单一品牌数据,删除单一品牌数据:
路由:
re_path(r"^goods/brands/(?P<pk>\d+)/$", Brands_views.as_view({
'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
还没有评论,来说两句吧...