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_name
    def __str__(self):
        return self.name

class 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_name
    def __str__(self):
        return self.name

class 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_name
    def __str__(self):
        return self.name

class 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_name
    def __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, Type4

class Type1ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Type1
        fields = '__all__'

class Type2ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Type2
        fields = '__all__'

class Type3ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Type3
        fields = '__all__'

class Type4ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Type4
        fields = '__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, BrowsableAPIRenderer


class 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=temp4
      this.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_name

    def __str__(self):
        return self.name
  • 迁移
python manage.py makemigrations
python manage.py migrate

3.2 数据导入

  • 序列化
class TypeModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Type
        fields = '__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=name
        type1.category_Type = category_type
        if parent_category_id:
            parent_category = Type.objects.filter(id=parent_category_id).first()
            type1.parent_category=parent_category
        type1.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 = this

      Axios.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].id
      for(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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>