- https://blog.csdn.net/weixin_45905671/article/details/115052054
{
"1":{ // 数字编号表示'组号',即对'一级目录'进行分组
"channels":[ // 一级目录
{"id":1, "name":"手机", "url":"http://shouji.jd.com/"},
{"id":2, "name":"相机", "url":"http://www.baidu.cn/"}
],
"sub_cats":[
{
"id":38,
"name":"手机通讯", // 二级目录
"sub_cats":[
{"id":115, "name":"手机"}, // 三级目录
{"id":116, "name":"游戏手机"}
]
},
{
"id":39,
"name":"手机配件", // 二级目录
"sub_cats":[
{"id":119, "name":"手机壳"},// 三级目录
{"id":120, "name":"贴膜"}
]
}
]
},
"2":{
"channels":[],
"sub_cats":[]
}
}
### models.py
class GoodsCategory(BaseModel):
"""
商品类别(分组)
"""
name = models.CharField(max_length=10, verbose_name='名称')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父类别')
class Meta:
db_table = 'tb_goods_category'
verbose_name = '商品类别'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class GoodsChannel(BaseModel):
"""
商品频道
"""
group_id = models.IntegerField(verbose_name='组号')
category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='顶级商品类别')
url = models.CharField(max_length=50, verbose_name='频道页面链接')
sequence = models.IntegerField(verbose_name='组内顺序')
class Meta:
db_table = 'tb_goods_channel'
verbose_name = '商品频道'
verbose_name_plural = verbose_name
def __str__(self):
return self.category.name
视图
逻辑如下: 要查询数据库,把数据手动拼接成上述结构返回给前端
### views
from django.shortcuts import render
from django.views import View
from goods.models import GoodsCategory, GoodsChannel
from collections import OrderedDict
class IndexView(View):
"""首页广告"""
def get(self, request):
"""提供首页页面"""
# 查询并展示商品分类
# categories = {} # 优化 OrderedDict 由无序变成有序
categories = OrderedDict()
# 查询所有的商品频道
channels = GoodsChannel.objects.order_by('group_id', 'sequence')
for channel in channels:
# 37个频道 11个组
group_id = channel.group_id
# print(group_id)
if group_id not in categories:
categories[group_id] = {'channels': [], 'sub_cats': []}
# print(categories)
cat1 = channel.category
categories[group_id]['channels'].append(
{
"id": cat1.id,
"name": cat1.name,
"url": channel.url
}
)
# print(categories) # 打印结果一级分类完成
# 查询二级和三级类别
# 查询二级 parent_id = cat1.id
# for cat2 in cat1.subs.all(): 此行简写代码可替换下面一行代码 models.py中定义了related_name=subs
for cat2 in GoodsCategory.objects.filter(parent_id=cat1.id).all():
cat2.sub_cats = []
categories[group_id]["sub_cats"].append(
{
"id": cat2.id,
"name": cat2.name,
"sub_cats": cat2.sub_cats
}
)
# for cat3 in cat2.subs.all():
for cat3 in GoodsCategory.objects.filter(parent_id=cat2.id).all():
cat2.sub_cats.append(
{
"id": cat3.id,
"name": cat3.name,
}
)
print(categories)
'''
{
"1":{
"channels":[
{"id":1, "name":"手机", "url":"http://shouji.jd.com/"},
{"id":2, "name":"相机", "url":"http://www.baidu.cn/"}
],
"sub_cats":[
{
"id":38,
"name":"手机通讯",
"sub_cats":[
{"id":115, "name":"手机"},
{"id":116, "name":"游戏手机"}
]
},
{
"id":39,
"name":"手机配件",
"sub_cats":[
{"id":119, "name":"手机壳"},
{"id":120, "name":"贴膜"}
]
}
]
},
'''
context = {
"categories": categories
}
return render(request, 'index.html', context=context)
<ul class="sub_menu">
{% for group in categories.values %}
<li>
<div class="level1">
{% for channel in group.channels %}
<a href="{{ channel.url }}">{{ channel.name }}</a>
{% endfor %}
</div>
<div class="level2">
{% for cat2 in group.sub_cats %}
<div class="list_group">
<div class="group_name fl">{{ cat2.name }} ></div>
<div class="group_detail fl">
{% for cat3 in cat2.sub_cats %}
<a href="/list/{{ cat3.id }}/1/">{{ cat3.name }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</li>
{% endfor %}
</ul>
- 问题,如果使用
DRF
框架来实现,数据结构如下,你要怎么写
{
"1":{
"channels":[
{"id":1, "name":"手机", "url":"http://shouji.jd.com/"},
{"id":2, "name":"相机", "url":"http://www.baidu.cn/"}
],
"sub_cats":[
{
"id":38,
"name":"手机通讯",
"sub_cats":[
{"id":115, "name":"手机"},
{"id":116, "name":"游戏手机"}
]
},
{
"id":39,
"name":"手机配件",
"sub_cats":[
{"id":119, "name":"手机壳"},
{"id":120, "name":"贴膜"}
]
}
]
},
"2":{
"channels":[],
"sub_cats":[]
}
}
- 思路分析: 分别序列化 GoodsCategory 和 GoodsChannel,再进行拼接
- 利用
ReadOnlyModelViewSet
实现商品分类所有数据的查询 / 单个商品分类查询(即,查所有 / 查单个)
### goods.urls
from django.conf.urls import url
from rest_framework.routers import DefaultRouter
from . import views
urlpatterns = [
......
]
router = DefaultRouter() # 使用默认路由
router.register(r'goods_type',views.GoodsTypeViewSet,basename='goods_type')
urlpatterns += router.urls
### views
from rest_framework.viewsets import ReadOnlyModelViewSet
from .serilizers import GoodsCategoryModelSerilizer,GoodsCategoryDetailModelSerilizer
from .models import GoodsCategory
class GoodsTypeViewSet(ReadOnlyModelViewSet):
queryset = GoodsCategory.objects.get_queryset()
def get_serializer_class(self):
# 根据action选择不同的序列化器
# queryset就无须这样处理,因为DRF已经帮我们做了
if self.action == 'list':
return GoodsCategoryModelSerilizer
else:
return GoodsCategoryDetailModelSerilizer
### serializers
from rest_framework import serializers
from .models import GoodsCategory
# 查所有
class GoodsCategoryModelSerilizer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = ('id','name')
# 查单个
class GoodsCategoryDetailModelSerilizer(serializers.ModelSerializer):
cats = serializers.SerializerMethodField()
class Meta:
model = GoodsCategory
fields = ('id','name','cats')
def get_cats(self,obj):
subs = obj.subs.all()
data = [] # 初始化最终返回的数据
for sub in subs: # 获取第二层数据
data_dict = {}
data_dict['name'] = sub.name
data_dict['id'] = sub.id
data_dict['cats'] = []
for sun in sub.subs.all(): # 获取第三层数据
data_sun = {}
data_sun['name'] = sun.name
data_sun['id'] = sun.id
data_dict['cats'].append(data_sun)
data.append(data_dict)
return data
- url: http://127.0.0.1:8001/goods_type/ (查所有)
[
{
"id": 1,
"name": "手机"
},
{
"id": 2,
"name": "相机"
},
{
"id": 3,
"name": "数码"
},
......
- url: http://127.0.0.1:8001/goods_type/1/ (查单个)
{
"id": 1,
"name": "手机",
"cats": [
{
"name": "手机通讯",
"id": 38,
"cats": [
{
"name": "手机",
"id": 115
},
{
"name": "游戏手机",
"id": 116
},
......
]
},
{
"name": "手机配件",
"id": 39,
"cats": [
{
"name": "手机壳",
"id": 119
},
{
"name": "贴膜",
"id": 120
},
......
]
}
]
}
- DRF 响应的数据格式中
- 如果是查所有,返回的数据格式就是一个list里面,放N个dict,dict里面再放各种东东
- 如果是查单个,返回的数据格式就是dict里面,放N个key和value存储各种东东
解决刚才存留的问题,我们在序列化器
手动构造数据格式
### serilizers
......
class GoodsCategoryModelSerilizer(serializers.ModelSerializer):
cats = serializers.SerializerMethodField()
class Meta:
model = GoodsCategory
fields = ('id','name','cats')
def get_cats(self,obj): # obj即每一个对象
subs = obj.subs.all()
data = []
for sub in subs:
data_dict = {}
data_dict['name'] = sub.name
data_dict['id'] = sub.id
data_dict['cats'] = []
for sun in sub.subs.all():
data_sun = {}
data_sun['name'] = sun.name
data_sun['id'] = sun.id
data_dict['cats'].append(data_sun)
# data_dict['sun']['name'] = sun.name
# data_dict['sun']['id'] = sun.id
data.append(data_dict)
return data
- http://127.0.0.1:8001/goods_type/
[
{
"id": 1,
"name": "手机",
"cats": [
{
"name": "手机通讯",
"id": 38,
"cats": [
{
"name": "手机",
"id": 115
},
{
"name": "游戏手机",
"id": 116
},
{
"name": "老人机",
"id": 117
},
{
"name": "对讲机",
"id": 118
}
]
},
{
"name": "手机配件",
"id": 39,
"cats": [
{
"name": "手机壳",
"id": 119
},
{
"name": "贴膜",
"id": 120
},
{
"name": "手机存储卡",
"id": 121
},
{
"name": "数据线",
"id": 122
},
{
"name": "充电器",
"id": 123
},
{
"name": "无线充电器",
"id": 124
},
{
"name": "手机耳机",
"id": 125
},
{
"name": "移动电源",
"id": 126
},
{
"name": "手机支架",
"id": 127
}
]
}
]
},
{
"id": 2,
"name": "相机",
"cats": [
{
"name": "摄影摄像",
"id": 40,
"cats": [
{
"name": "数码相机",
"id": 128
},
{
"name": "微单相机",
"id": 129
},
{
"name": "单反相机",
"id": 130
},
{
"name": "拍立得",
"id": 131
},
{
"name": "运动相机",
"id": 132
},
{
"name": "摄像机",
"id": 133
},
{
"name": "镜头",
"id": 134
},
{
"name": "数码相框",
"id": 135
}
]
}
]
},
......
]
- 进一步简化前面所写的
view
,只留一个序列化器
即可
- 若不传
pk
,就查所有(包含子集)
- 若传
pk
,就查单个(包含子集)
### views
......
class GoodsTypeViewSet(ReadOnlyModelViewSet):
queryset = GoodsCategory.objects.get_queryset()
serializer_class = GoodsCategoryModelSerilizer
# def get_serializer_class(self):
# if self.action == 'list':
# return GoodsCategoryModelSerilizer
# else:
# return GoodsCategoryDetailModelSerilizer
- 小结:对于查
单个
或者查所有
这种接口,可以使用ReadOnlyModelViewSet
来搞定
- 写法上,指定所有的 queryset 和 serializer_class 即可
class GoodsTypeViewSet(ReadOnlyModelViewSet):
queryset = GoodsCategory.objects.get_queryset()
serializer_class = GoodsCategoryModelSerilizer
- 子集的'cats'字段需要自己手动构造数据来实现(类似三级联动这种数据,嵌套两层循环即可)
class GoodsCategoryModelSerilizer(serializers.ModelSerializer):
cats = serializers.SerializerMethodField()
class Meta:
model = GoodsCategory
fields = ('id','name','cats')
def get_cats(self,obj):
subs = obj.subs.all()
data = []
for sub in subs:
data_dict = {}
data_dict['name'] = sub.name
data_dict['id'] = sub.id
data_dict['cats'] = []
for sun in sub.subs.all():
data_sun = {}
data_sun['name'] = sun.name
data_sun['id'] = sun.id
data_dict['cats'].append(data_sun)
# data_dict['sun']['name'] = sun.name
# data_dict['sun']['id'] = sun.id
data.append(data_dict)
return data
商品频道
的三级联动数据展示
### urls
......
urlpatterns = [
......
]
router = DefaultRouter()
router.register(r'goods_type',views.GoodsTypeViewSet,basename='goods_type')
# 增加接口
router.register(r'channel_goods',views.ChannelGoodsViewSet,basename='channel_goods')
urlpatterns += router.urls
### views
......
class ChannelGoodsViewSet(ModelViewSet):
'''
- ModelViewSet提供了 增删改(更新所有/更新局部)查(查所有/查单个) 全套服务
'''
# 排序
queryset = GoodsChannel.objects.get_queryset().order_by('group_id')
serializer_class = ChannelGoodsModelSerilizer
### serializers
......
class ChannelGoodsModelSerilizer(serializers.ModelSerializer):
category = GoodsCategoryModelSerilizer() # 引用之前的序列化器
class Meta:
model = GoodsChannel
exclude = ['sequence']
- 查所有: http://127.0.0.1:8001/channel_goods/
[
{
"id": 1,
"category": {
"id": 1,
"name": "手机",
"cats": [
{
"name": "手机通讯",
"id": 38,
"cats": [
{
"name": "手机",
"id": 115
},
......
{
"name": "对讲机",
"id": 118
}
]
},
{
"name": "手机配件",
"id": 39,
"cats": [
{
"name": "手机壳",
"id": 119
},
......
{
"name": "手机支架",
"id": 127
}
]
}
]
},
"create_time": "2018-04-09T09:15:38.057078Z",
"update_time": "2018-04-09T09:15:38.057150Z",
"group_id": 1,
"url": "http://shouji.jd.com"
},
{......}
]
- 查单个: http://127.0.0.1:8001/channel_goods/1/
{
"id": 1,
"category": {
"id": 1,
"name": "手机",
"cats": [
{
"name": "手机通讯",
"id": 38,
"cats": [
{
"name": "手机",
"id": 115
},
......
{
"name": "对讲机",
"id": 118
}
]
},
{
"name": "手机配件",
"id": 39,
"cats": [
{
"name": "手机壳",
"id": 119
},
......
{
"name": "手机支架",
"id": 127
}
]
}
]
},
"create_time": "2018-04-09T09:15:38.057078Z",
"update_time": "2018-04-09T09:15:38.057150Z",
"group_id": 1,
"url": "http://shouji.jd.com"
}