浮光商城04-导航功能实现

接下来实现项目的导航功能,包括头部导航和脚部导航。

效果图
E-R图,如下:

image-20220402021856478

01. 创建模型

image-20220402022526081

导航数据的存储,必须要找出导航数据的结构,

也就是有哪些属性?

  • 导航位置(头部或脚部)
  • 导航名称
  • 导航链接
  • 导航序号(用于排序)
  • 是否显示
  • 是否外链
  • 添加时间
  • 更新时间
  • 是否删除(逻辑删除)

home/models.py,代码:

from fuguang_api.utils.models import models, BaseModel


# Create your models here.
class Nav(BaseModel):
    """导航菜单"""
    # 字段选项
    # 模型对象.<字段名>  ---> 实际数据
    # 模型对象.get_<字段名>_display()  --> 文本提示
    POSITION_OPTION = (
        # (实际数据, "文本提示"),
        (0, "顶部导航"),
        (1, "脚部导航"),
    )
    link = models.CharField(max_length=255, verbose_name="导航连接")
    is_http = models.BooleanField(default=False, verbose_name="是否是外部链接")
    position = models.IntegerField(choices=POSITION_OPTION, default=0)

    class Meta:
        db_table = "fg_nav"
        verbose_name = "导航菜单"
        verbose_name_plural = verbose_name

实际开发过程中,不同的模型类往往有很多相同的字段,此时可以使用抽象模型。

例如,上面的Nav类没有继承models.Model,而是继承BaseModel;

BaseModel是一个公共模型;【抽象模型,不会在数据迁移的时候为它创建表】,

保存在项目的公共代码库目录下,luffycityapi/utils.py文件中。

luffycityapi/utils/models.py,代码:

from django.db import models


class BaseModel(models.Model):
    """
    公共模型
    保存项目中的所有模型的公共属性和公共方法的声明
    """
    title = models.CharField(max_length=255, default="", verbose_name="名称/标题")
    is_deleted = models.BooleanField(default=False, verbose_name="是否删除")
    orders = models.IntegerField(default=0, verbose_name="序号")
    is_show = models.BooleanField(default=True, verbose_name="是否显示")
    # auto_now_add=True 当数据被创建时,以当前时间作为默认值写入当前字段
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
    # auto_now=True 当数据被更新时,以当前时间作为值写入当前字段
    updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")

    class Meta:
        # 设置当前模型类并非真正的模型,而是一种保存公共代码的抽象模型类
        # 这种模型在数据迁移中不会被当做数据模型来创建数据表
        abstract = True

在开发中,对于删除数据,我们往往使用逻辑删除,而不是真正从磁盘上物理删除数据。

我们可以把utils设置为系统导包路径,方便我们后面开发——导包时可以简写路径。

在settings/dev.py中配置导包路径,代码:

import sys
sys.path.insert(0, str( BASE_DIR / "apps") )  # 将apps设置为导包路径
sys.path.insert(0, str( BASE_DIR / "utils") )  # 将utils设置为导包路径

home/models.py中,导包路径便可简写:

# 原来的
from fuguang_api.utils.models import models, BaseModel
# 修改为
from models import BaseModel, models

创建好了模型类,接下来进行数据库迁移。

实际工作中,有些大厂企业不需要我们使用django的数据迁移操作,

如果公司有DBA的话,往往DBA已经提前设计好数据表结构了,

我们只需要根据表结构声明模型代码,保证模型代码的表名、字段与数据库中的表结构对应上,就可以在django中直接调用ORM模型对象操作数据库中的数据了。

cd ~/Desktop/fuguang/fuguang_api
python manage.py makemigrations
python manage.py migrate

上面仅仅创建的是数据表结构而已,接下来我们如果要实现客户端展示导航功能,

还需要在admin后台手动添加测试数据,或者在MySQL交互终端下添加测试数据。

测试数据如下:

INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (1, '免费课', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 0);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (2, '进阶课', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/project', 0, 0);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (3, '大师课', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/position', 0, 0);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (4, '习题库', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/exam', 0, 0);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (5, '浮光学院', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', 'https://www.fuguang.com', 1, 0);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (6, '企业服务', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (7, '关于我们', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (8, '联系我们', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (9, '商务合作', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (10, '帮助中心', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (11, '意见反馈', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);
INSERT INTO fuguang.fg_nav (id, title, orders, is_show, is_deleted, created_time, updated_time, link, is_http, position) VALUES (12, '新手指南', 1, 1, 0, '2021-07-15 01:27:27.350000', '2021-07-15 01:27:28.690000', '/free', 0, 1);

02. 序列化器

home.serializers,代码:

from rest_framework import serializers
from .models import Nav


class NavModelSerializer(serializers.ModelSerializer):
    """
    导航菜单的序列化器
    """
    class Meta:
        model = Nav
        fields = ["title", "link", "is_http"]

03. 视图代码

home/views.py

import constants
from rest_framework.generics import ListAPIView
from .models import Nav
from .serializers import NavModelSerializer


class NavHeaderListAPIView(ListAPIView):
    """顶部导航视图"""
    queryset = Nav.objects.filter(position=constants.NAV_HEADER_POSITION, is_show=True, is_deleted=False).order_by("orders", "-id")[:constants.NAV_HEADER_SIZE]
    serializer_class = NavModelSerializer


class NavFooterListAPIView(ListAPIView):
    """脚部导航视图"""
    queryset = Nav.objects.filter(position=constants.NAV_FOOTER_POSITION, is_show=True, is_deleted=False).order_by("orders", "-id")[:constants.NAV_FOOTER_SIZE]
    serializer_class = NavModelSerializer

3.1 常量配置

开发过程中,使用到的一些产量,往往是统一存放于一个文件中,以导包的形式调用,以方便修改。

utils/constants.py,代码:

"""常量配置文件"""
# 导航的位置 --> 顶部
NAV_HEADER_POSITION = 0
# 导航的位置 --> 脚部
NAV_FOOTER_POSITION = 1
# 顶部导航显示的最大数量
NAV_HEADER_SIZE = 5
# 脚部导航显示的最大数量
NAV_FOOTER_SIZE = 10

04. 路由代码

home/urls.py

from django.urls import path
from . import views
urlpatterns = [
    path("nav/header/", views.NavHeaderListAPIView.as_view()),
    path("nav/footer/", views.NavFooterListAPIView.as_view()),
]

完成了上面操作以后,可以直接访问url地址或者通过postman来请求测试接口数据是否正确。

image-20220407004245180

05. 客户端获取导航数据

所有与api服务端进行交互的操作代码,可以单独保存api目录下,根据数据表的不同单独创建js文件,方便将来代码复用。

src/api/nav.js,代码:

import http from "../utils/http"
import {reactive, ref} from "vue"

const nav = reactive({
    header_nav_list: [], // 头部导航列表
    footer_nav_list: [], // 脚部导航列表
    get_header_nav(){
        // 获取头部导航
        return http.get("/home/nav/header/")
    },
    get_footer_nav(){
        // 获取脚部导航
        return http.get("/home/nav/footer/")
    },

})

export default nav;

components/Header.vue代码:

<template>
    <div class="header-box">
      <div class="header">
        <div class="content">
          <div class="logo">
            <router-link to="/"><img src="../assets/logo.svg" alt=""></router-link>
          </div>
          <ul class="nav">
              <li v-for="nav in nav.header_nav_list">
                <a :href="nav.link" v-if="nav.is_http">{{nav.title}}</a>
                <router-link :to="nav.link" v-else>{{nav.title}}</router-link>
              </li>
          </ul>
          <div class="search-warp">
            <div class="search-area">
              <input class="search-input" placeholder="请输入关键字..." type="text" autocomplete="off">
              <div class="hotTags">
                <router-link to="/search/?words=Vue" target="_blank" class="">Vue</router-link>
                <router-link to="/search/?words=Python" target="_blank" class="last">Python</router-link>
              </div>
            </div>
            <div class="showhide-search" data-show="no"><img class="imv2-search2" src="../assets/search.svg" /></div>
          </div>
          <div class="login-bar">
            <div class="shop-cart full-left">
              <img src="../assets/cart.svg" alt="" />
              <span><router-link to="/cart">购物车</router-link></span>
            </div>
            <div class="login-box full-left">
              <span>登录</span>
              &nbsp;/&nbsp;
              <span>注册</span>
            </div>
          </div>
        </div>
      </div>
    </div>
</template>

<script setup>
import nav from "../api/nav";

// 请求头部导航列表
nav.get_header_nav().then(response=>{
  nav.header_nav_list = response.data
})


</script>

components/Footer.vue代码:

<template>
    <div class="footer">
      <ul>
        <li v-for="nav in nav.footer_nav_list">
          <a :href="nav.link" v-if="nav.is_http">{{nav.title}}</a>
          <router-link :to="nav.link" v-else>{{nav.title}}</router-link>
        </li>
      </ul>
      <p>Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p>
    </div>
</template>

<script setup>
import nav from "../api/nav";

// 获取脚部导航列表
nav.get_footer_nav().then(response=>{
  nav.footer_nav_list = response.data
})

</script>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值