django web经典模块开发实战——第三章 用Django设计大型电商的类别表

1 电商类别表的项目功能需求

1.1 使用vue.js在前端开发一个电商导航栏项目 demo1

  • nodejs的安装
yum install -y nodejs
  • 安装淘宝镜像(贼慢。。)
npm install -g cnpm --registry=HTTPS://registry.npm.taobao.org
  • 安装Vue.js的脚手架工具
cnpm install --globle vue-cli
  • 创建项目
vue init webpack-simple demo1
# 5次回车
  • 安装依赖
cd demo1
cnpm install
  • 运行
npm run dev
  • 访问

http://localhost:8080/

  • 修改App.vue

    • html部分
    <template><div id="app"><div class="all"><div class="one"><div class="oneType" v-for="(item, index) in one" :key="index"><b>{{one[index]}}</b></div></div><div class="twothreefour"><div class="two"><div class="twoType" v-for="(item,index) in two" :key="index" @mouseenter="open(index)"><b>{{two[index]}}</b></div></div><div class="threefour" v-if="flag" @mouseleave="close()"><div class="threefourType" v-for="(item, index) in three" :key="index"><span class="three">{{three[index]}}</span><span class="four" v-for="(item4, index4) in four" :key="index4">{{four[index4]}}</span></div></div></div></div></div>
    </template>
    
    • js部分
    <script>
    export default {name: 'app',data () {return {one:['一级类目', '一级类目', '一级类目', '一级类目', '一级类目'],two:['二级类目1', '二级类目2', '二级类目3', '二级类目4', '二级类目5'],three:[],four:['四级类目','四级类目','四级类目','四级类目','四级类目'],flag:false}}
    }
    </script>
    
    • css部分
    <style>
    *{/* 样式初始化 */box-sizing:border-box;margin:0;padding:0;
    }
    .all{/* 将整个导航栏组件做整体设置 *//* 宽度占浏览器80%, 高度400px,背景灰色,上边距50px,左右居中 *//* 设置为flex弹性盒子,并且定义为高度不够自动折行模式,用于横向排列子元素 */width: 80%;height: 400px;background: #eee;margin: 50px auto;display: -webkit-flex;display: flex;flex-wrap: wrap;
    }
    .one{/* 设置一级类目所占地区的样式,宽度占满all盒子的100% */width: 100%;height: 50px;background: #FF8888;display: flex;display: -webkit-flex;flex-wrap: wrap;/* 弹性盒子内部的子元素都均匀排列成一横排,并且左右两边都留有一定空隙 */justify-content: space-around;
    }
    .oneType{width: 20%;height: 50px;line-height: 50px;text-align: center;
    }
    .oneType:hover{background-color:chocolate;color: #eee;
    }
    .twothreefour{/* 盛放二、三、四级目录的盒子 */width: 100%;height: 350px;background: #66ff66;display: -webkit-flex;display: flex;flex-wrap: wrap;/* 弹性盒子内部的子元素都均匀排列成一横排,并且左右两边都不留空隙 */justify-content: space-between;
    }
    .two{/* 设置盛放二级类目录的弹性盒子 */width: 15%;height: 100%;background: #77FFCC;display: -webkit-flex;display: flex;/* 弹性盒子内部的子元素从上到下排成一列 */flex-direction: column;
    }
    .twoType{width: 100%;height: 40px;line-height: 40px;text-align: center;background: #EEFFBB;
    }
    .twoType:hover{background-color:black;color: #eee;
    }
    .threefour{width: 40%;margin-right: 45%;height: 100%;background: #33FFDD;display: -webkit-flex;display: flex;flex-direction: column;
    }
    .threefourType{margin: 10px auto;
    }
    .three{font-family: 微软雅黑, 黑体;font-size: 16px;font-weight: 800;
    }
    .four{font-family: 宋体;font-size: 12px;font-weight: 400;
    }
    </style>
    
  • 重新运行项目访问网址

2 为什么不用传统方式建类别表

2.1 新建后端演示项目demo2

  • settings中的注册
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01','rest_framework'
]
  • app01/models中新建表
class Type1(models.Model):"""一级目录"""name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别'verbose_name_plural = verbose_namedef __str__(self):return self.nameclass Type2(models.Model):"""二级类目"""parent = models.ForeignKey(Type1, verbose_name='父级类别', null=True, blank=True, on_delete=models.CASCADE)name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别2'verbose_name_plural = verbose_namedef __str__(self):return self.nameclass Type3(models.Model):"""三级类目"""parent = models.ForeignKey(Type2, verbose_name='父级类别', null=True, blank=True, on_delete=models.CASCADE)name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别3'verbose_name_plural = verbose_namedef __str__(self):return self.nameclass Type4(models.Model):"""三级类目"""parent = models.ForeignKey(Type3, verbose_name='父级类别', null=True, blank=True, on_delete=models.CASCADE)name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别4'verbose_name_plural = verbose_namedef __str__(self):return self.name
  • 迁移
python manage.py makemigrations
python manage.py migrate
  • 添加表中数据
INSERT INTO "app01_type1" VALUES(1,'京东生鲜',1575302400000);
INSERT INTO "app01_type1" VALUES(2,'亚马逊',1575302400000);
INSERT INTO "app01_type1" VALUES(3,'天狗超市',1575302400000);
INSERT INTO "app01_type1" VALUES(4,'淘气网',1575302400000);
INSERT INTO "app01_type1" VALUES(5,'苏宁难购',1575302400000);
INSERT INTO "app01_type2" VALUES(1,'鞋子箱包',1575302400000,3);
INSERT INTO "app01_type2" VALUES(2,'零食',1575388800000,3);
INSERT INTO "app01_type2" VALUES(3,'美妆丽人',1575388800000,3);
INSERT INTO "app01_type2" VALUES(4,'男装女装',1575388800000,3);
INSERT INTO "app01_type2" VALUES(5,'厨卫',1575388800000,3);
INSERT INTO "app01_type3" VALUES(1,'坚果',1575388800000,2);
INSERT INTO "app01_type3" VALUES(2,'夹克衫',1575388800000,4);
INSERT INTO "app01_type3" VALUES(3,'收纳箱',1575388800000,1);
INSERT INTO "app01_type3" VALUES(4,'纱裙',1575388800000,4);
INSERT INTO "app01_type3" VALUES(5,'长裤',1575388800000,4);
INSERT INTO "app01_type4" VALUES(1,'三只老鼠',1575388800000,1);
INSERT INTO "app01_type4" VALUES(2,'仙女白凤裙',1575388800000,4);
INSERT INTO "app01_type4" VALUES(3,'双鹰同款黑皮风衣',1575388800000,2);
INSERT INTO "app01_type4" VALUES(4,'性感黑纱',1575388800000,4);
INSERT INTO "app01_type4" VALUES(5,'折纸箱',1575388800000,3);
INSERT INTO "app01_type4" VALUES(6,'旅行箱',1575388800000,3);
INSERT INTO "app01_type4" VALUES(7,'朦胧美纱裙',1575388800000,4);
INSERT INTO "app01_type4" VALUES(8,'水洗布单裤',1575388800000,5);
INSERT INTO "app01_type4" VALUES(9,'牛仔裤',1575388800000,5);
INSERT INTO "app01_type4" VALUES(10,'闪亮皮夹克',1575388800000,2);
INSERT INTO "app01_type4" VALUES(11,'黑又亮夹克',1575388800000,2);

2.2 完善demo2的后台逻辑代码

  • serializers
from rest_framework import serializers
from .models import Type1, Type2, Type3, Type4class Type1ModelSerializer(serializers.ModelSerializer):class Meta:model = Type1fields = '__all__'class Type2ModelSerializer(serializers.ModelSerializer):class Meta:model = Type2fields = '__all__'class Type3ModelSerializer(serializers.ModelSerializer):class Meta:model = Type3fields = '__all__'class Type4ModelSerializer(serializers.ModelSerializer):class Meta:model = Type4fields = '__all__'
  • views
from django.shortcuts import render
from .serializers import Type1ModelSerializer, Type2ModelSerializer, Type3ModelSerializer, Type4ModelSerializer
from .models import Type1, Type4, Type3, Type2
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer, BrowsableAPIRendererclass Type1View(APIView):"""all Type1"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type1.objects.all()Types_serializer = Type1ModelSerializer(Types, many=True)return Response(Types_serializer.data)class Type2View(APIView):"""all Type2"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type2.objects.all()Types_serializer = Type2ModelSerializer(Types, many=True)return Response(Types_serializer.data)class Type3View(APIView):"""all Type3"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type3.objects.all()Types_serializer = Type3ModelSerializer(Types, many=True)return Response(Types_serializer.data)class Type4View(APIView):"""all Type4"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type4.objects.all()Types_serializer = Type4ModelSerializer(Types, many=True)return Response(Types_serializer.data)
  • url

    urlpatterns = {url(r'^api/', include('app01.urls', namespace='app01'))
    }
    
    urlpatterns = [url(r'^Type1/$', views.Type1View.as_view(), name='type1'),url(r'^Type2/$', views.Type2View.as_view(), name='type2'),url(r'^Type3/$', views.Type3View.as_view(), name='type3'),url(r'^Type4/$', views.Type4View.as_view(), name='type4'),
    ]
    

2.3 前后端项目联合调试

  • 给demo1项目安装axios
cnpm install axios -save
  • App.vue的修改

html

<template><div id="app"><div class="all"><div class="one"><div class="oneType" v-for="(item, index) in one" :key="index"><b>{{one[index].name}}</b></div></div><div class="twothreefour"><div class="two"><div class="twoType" v-for="(item,index) in two" :key="index" @mouseenter="open(index)"><b>{{two[index].name}}</b></div></div><div class="threefour" v-if="flag" @mouseleave="close()"><div class="threefourType" v-for="(item, index) in three1" :key="index"><span class="three">{{three1[index]}}</span><span class="four" v-for="(item4, index4) in four1" :key="index4">{{four1[index4]}}&nbsp;</span></div></div></div></div></div>
</template>

js

<script>
import Axios from 'axios';
export default {name: 'app',data () {return {one:[],two:[],three:[],four:[],flag:false,three1:[],four1:[]}},methods: {getData() {const api='http://127.0.0.1:8000/';var api1=api+'api/Type1/';var api2=api+'api/Type2/';var api3=api+'api/Type3/';var api4=api+'api/Type4/';var Type1=[];var Type2=[];var Type3=[];var Type4=[];Axios.get(api1).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type1.push(response.data[i])}// console.log(Type1)}).catch(function (error) {console.log(error);});this.one=Type1;Axios.get(api2).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type2.push(response.data[i])}// console.log(Type2)}).catch(function (error) {console.log(error);});this.two=Type2;Axios.get(api3).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type3.push(response.data[i])}// console.log(Type3)}).catch(function (error) {console.log(error);});this.three=Type3;Axios.get(api4).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type4.push(response.data[i])}// console.log(Type4)}).catch(function (error) {console.log(error);});this.four=Type4;// console.log(this.one)// console.log(this.two)// console.log(this.three)// console.log(this.four)},open(index) {// console.log(this.two[index].id)var temp=[]for (var i=0;i<this.three.length;i++) {if(this.three[i].parent===index+1) {temp.push(this.three[i].name)}}console.log(temp)this.three1=temp;var temp4=[]for (var j=0;j<this.four.length;j++) {temp4.push(this.four[j].name)}this.four1=temp4this.flag=true},close() {this.flag=false}},mounted() {this.getData()},
}
</script>

注:四级类目没有筛选赋值

  • 解决跨域问题
pip install Django-cors-headers

settings

INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01','rest_framework','corsheaders'
]MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware','django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]CORS_ORIGIN_ALLOW_ALL = True
  • 前后端同时运行访问即可见结果

  • 小结

    从本节不难看出,为了获取数据,前端通过不同的4个API发送了4次网络请求,这是因为我们假设一个电商平台只有四级类目。但是现实中者显然是不肯能的,往往一个用户量越庞大的电商平台,商品的种类越齐全、越细分,就意味着发送网络请求的倍数也就越多,需要的带宽也就更多,这个成本是非常庞大的。

3 使用Django的model实现类别表建立

3.1 四表合一

  • 新建model
class Type(models.Model):"""商品类别"""CATEGORY_TYPE = ((1, '一级类目'),(2, '二级类目'),(3, '三级类目'),(4, '四级类目'),)name = models.CharField(default='', max_length=30, verbose_name='类别名', help_text='类别名')code = models.CharField(default='', max_length=30, verbose_name='类别code', help_text='类别code')desc = models.CharField(default='', max_length=30, verbose_name='类别描述', help_text='类别描述')category_Type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name='类别描述', help_text='类别描述')parent_category = models.ForeignKey('self', null=True, blank=True, verbose_name='父类目录', help_text='父类别', related_name='sub_cat', on_delete=models.CASCADE)is_tab = models.BooleanField(default=False, verbose_name='是否导航', help_text='是否导航')class Meta:verbose_name = '商品类别'verbose_name_plural = verbose_namedef __str__(self):return self.name
  • 迁移
python manage.py makemigrations
python manage.py migrate

3.2 数据导入

  • 序列化
class TypeModelSerializer(serializers.ModelSerializer):class Meta:model = Typefields = '__all__'
  • 视图
class TypeView(APIView):"""操作类别表"""renderer_classes = [JSONRenderer]def get(self, request, format=None):types = Type.objects.all()types_serializer = TypeModelSerializer(types, many=True)return Response(types_serializer.data)def post(self, request):name = request.data.get('name')category_type = request.data.get('lei')parent_category_id = request.data.get('parent')type1 = Type()type1.name=nametype1.category_Type = category_typeif parent_category_id:parent_category = Type.objects.filter(id=parent_category_id).first()type1.parent_category=parent_categorytype1.save()type_serializer = TypeModelSerializer(type1)return Response(type_serializer.data)
  • 路由
url(r'^type/$', views.TypeView.as_view(), name='type'),
  • 可通过postman post方式来插入数据
  • 这里直接给出导入完成的sql
INSERT INTO "app01_type" VALUES(1,'天狗超市','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(2,'京东生鲜','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(3,'苏宁难购','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(4,'亚马逊','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(5,'淘气网','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(6,'男装女装','','',2,0,1);
INSERT INTO "app01_type" VALUES(7,'鞋子箱包','','',2,0,1);
INSERT INTO "app01_type" VALUES(8,'零食','','',2,0,1);
INSERT INTO "app01_type" VALUES(9,'厨卫','','',2,0,1);
INSERT INTO "app01_type" VALUES(10,'美妆丽人','','',2,0,1);
INSERT INTO "app01_type" VALUES(11,'夹克衫','','',3,0,6);
INSERT INTO "app01_type" VALUES(12,'长裤','','',3,0,6);
INSERT INTO "app01_type" VALUES(13,'纱裙','','',3,0,6);
INSERT INTO "app01_type" VALUES(14,'收纳箱','','',3,0,7);
INSERT INTO "app01_type" VALUES(15,'坚果','','',3,0,8);
INSERT INTO "app01_type" VALUES(16,'双鹰同款黑夹克','','',4,0,11);
INSERT INTO "app01_type" VALUES(17,'闪亮皮夹克','','',4,0,11);
INSERT INTO "app01_type" VALUES(18,'黑又亮夹克','','',4,0,11);
INSERT INTO "app01_type" VALUES(19,'牛仔裤','','',4,0,12);
INSERT INTO "app01_type" VALUES(20,'水洗布单裤','','',4,0,12);
INSERT INTO "app01_type" VALUES(21,'朦胧美纱裙','','',4,0,13);
INSERT INTO "app01_type" VALUES(22,'仙女风白裙','','',4,0,13);
INSERT INTO "app01_type" VALUES(23,'性感黑纱','','',4,0,13);
INSERT INTO "app01_type" VALUES(24,'折纸箱','','',4,0,14);
INSERT INTO "app01_type" VALUES(25,'旅行箱','','',4,0,14);
INSERT INTO "app01_type" VALUES(26,'三只老鼠','','',4,0,15);

3.3 前后端项目联合调试

  • 在demo1/src/App.vue原来的基础上,只修改<script>标签内的代码
<script>
import Axios from 'axios';
export default {name: 'app',data () {return {type:[],one:[],two:[],flag:false,three1:[],four1:[]}},methods: {getData() {const api='http://127.0.0.1:8000/api/type/';var _this = thisAxios.get(api).then(function (response) {_this.type=response.data;for(var i=0;i<_this.type.length;i++){if(_this.type[i].category_Type===1){_this.one.push(_this.type[i])}}for(var j=0;j<_this.type.length;j++){if(_this.type[j].category_Type===2){_this.two.push(_this.type[j])}}}).catch(function (error) {console.log(error);});},open(index) {this.three1=[]this.four1=[]var parent=this.two[index].idfor(var i=0;i<this.type.length;i++){if(this.type[i].parent_category===parent){this.three1.push(this.type[i].name)}if(this.type[i].category_Type===4){this.four1.push(this.type[i].name)}}this.flag=true}},mounted() {this.getData()}
}
</script>

Published by

风君子

独自遨游何稽首 揭天掀地慰生平