renran首页

首页

对于首页要展示的数据和功能,我们先创建一个单独的子应用来完成。

cd renranapi/apps/
python ../../manage.py startapp home

注册子应用,settings/dev.py,代码:

INSTALLED_APPS = [

    'home',
]

轮播图功能实现

安装依赖模块和配置

图片处理模块

前面项目搭建时已经安装了,如果没有安装则需要安装

pip install pillow
上传文件相关配置

settings/dev.py

# 访问静态文件的url地址前缀
STATIC_URL = '/static/'
# 设置django的静态文件目录
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,"static")
]

# 项目中存储上传文件的根目录[暂时配置],注意,uploads目录需要手动创建否则上传文件时报错
MEDIA_ROOT=os.path.join(BASE_DIR,"uploads")
# 访问上传文件的url地址前缀
MEDIA_URL ="/media/"

在xadmin中输出上传文件的Url地址

总路由urls.py新增代码:

from django.urls import re_path
from django.conf import settings
from django.views.static import serve

urlpatterns = [
  	...
    re_path(r'media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),
]

创建轮播图的模型

home/models.py

from django.db import models

# Create your models here.
class Banner(models.Model):
    """
    轮播图
    """
    # upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to
    image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True,blank=True)
    name = models.CharField(max_length=150, verbose_name='轮播图名称')
    note = models.CharField(null=True, blank=True, max_length=150, verbose_name='备注信息')
    link = models.CharField(null=True, blank=True, max_length=150, verbose_name='轮播图广告地址')
    start_time = models.DateTimeField(verbose_name="上架时间",default=None, null=True, blank=True)
    end_time = models.DateTimeField(verbose_name="下架时间",default=None, null=True, blank=True)
    orders = models.IntegerField(default=0, verbose_name='显示顺序')
    is_http = models.BooleanField(verbose_name="是否站外地址", default=False, help_text="站内地址格式:/users/<br>站外地址格式:http://www.baidu.com")
    is_show=models.BooleanField(default=False, verbose_name="是否上架")
    is_delete=models.BooleanField(default=False, verbose_name="逻辑删除")

    class Meta:
        db_table = 'rr_banner'
        verbose_name = '轮播图'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

数据迁移

python manage.py makemigrations
python manage.py migrate

序列化器

home/serializers.py

from rest_framework import serializers
from .models import Banner
class BannerModelSerializer(serializers.ModelSerializer):
    """轮播图序列化器"""
    class Meta:
        model = Banner
        fields = ["image","link","is_http"]

视图代码

views.py

from django.shortcuts import render

# Create your views here.
from rest_framework.generics import ListAPIView
from .models import Banner
from datetime import datetime
from .serializers import BannerModelSerializer
from renranapi.settings import constants
class BannerListAPIView(ListAPIView):
    queryset = Banner.objects.filter(
        is_show=True,
        is_delete=False,
        start_time__lte=datetime.now(),
        end_time__gte=datetime.now()
    ).order_by("orders","-id")[:constants.HOME_BANNER_LENGTH]
    serializer_class = BannerModelSerializer

settings/constants.py代码:

# 首页轮播图显示个数
HOME_BANNER_LENGTH = 8

路由代码

home/urls.py

from django.urls import path
from . import views
urlpatterns = [
    path("banner/", views.BannerListAPIView.as_view()),
]

把home的路由urls.py注册到总路由

from django.contrib import admin
from django.urls import path,include,re_path

import xadmin
xadmin.autodiscover()

# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()

# 上文文件资源
from django.conf import settings
from django.views.static import serve

urlpatterns = [
    re_path(r'media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),
    path(r'xadmin/', xadmin.site.urls),
    # path('admin/', admin.site.urls),
    path('stu/', include("students.urls")),
    path('users/', include("users.urls")),
    path('', include("home.urls")),
]

访问http://api.renran.cn:8000/banner/,效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7AWnhPwq-1595161631872)(E:/python八期/北京python27期荏苒项目/day119/assets/1557457803586.png)]

在配置文件中注册如下应用

# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'

# 修改时区
TIME_ZONE = 'Asia/Shanghai'

# 关闭时区转换
USE_TZ = False

注册轮播图模型到xadmin中

home/adminx.py,代码:

import xadmin

from .models import Banner
class BannerModelAdmin(object):
    list_display = ["id","name","link","is_show","start_time","end_time"]
    list_editable = ["is_show","start_time","end_time"]
xadmin.site.register(Banner,BannerModelAdmin)
修改后端xadmin中子应用名称

home/apps.py

class HomeConfig(AppConfig):
    name = 'home'
    verbose_name = '我的首页'

__init__.py

default_app_config = "home.apps.HomeConfig"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Zque1kQ-1595161631874)(E:/python八期/北京python27期荏苒项目/day119/assets/1557459568731.png)]

给轮播图添加测试数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfLzGyr9-1595161631876)(E:/python八期/北京python27期荏苒项目/day119/assets/1557459669649.png)]

添加几条测试数据效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kTzTKhDm-1595161631880)(E:/python八期/北京python27期荏苒项目/day119/assets/1557459949073.png)]

经过上面的操作,我们就完成了轮播图的API接口,接下来,可以考虑提交一个代码版本.

但是,我们前面创建了一个本地开发分支,所以我们如果直接提交代码,默认是在dev分支提交的.

所以就不能直接使用git push,而是采用以下命令提交

git add .
git commit -m "服务端实现轮播图的API接口"
# 注意 dev就是dev:dev的简写  表示从本地的dev中把代码推送到线上分支dev,如果线上没有dev分支,则自动创建dev分支

客户端代码获取数据

Banner.vue代码:

<template>
  <div id="home">
    <Header></Header>
    <div class="container">
      <div class="row">
        <div class="main">
          <!-- Banner -->
          <div class="banner">
            <el-carousel height="272px" indicator-position="none" :interval="2000">
              <el-carousel-item v-for="banner,key in banner_list" :key="key">
                <a :href="banner.link" v-if="banner.is_http" target="_blank"><img :src="banner.image"></a>
                <router-link :to="banner.link" v-else><img :src="banner.image"></router-link>
              </el-carousel-item>
            </el-carousel>
          </div>
          <div id="list-container">
            <!-- 文章列表模块 -->
            <ul class="note-list">
              <li class="">
                <div class="content">
                  <a class="title" target="_blank" href="">常做此运动,让你性福加倍</a>
                  <p class="abstract">运动,是人类在发展过程中有意识地对自己身体素质的培养的各种活动 运动的方式多种多样 不仅仅是我们常知的跑步,球类,游泳等 今天就为大家介绍一种男...</p>
                  <div class="meta">
                    <span class="jsd-meta">
                      <img src="/static/image/paid1.svg" alt=""> 4.8
                    </span>
                    <a class="nickname" target="_blank" href="">上班族也健身</a>
                    <a target="_blank" href="">
                      <img src="/static/image/comment.svg" alt=""> 4
                    </a>
                    <span><img src="/static/image/like.svg" alt=""> 31</span>
                  </div>
                </div>
              </li>
              <li class="have-img">
                <a class="wrap-img" href="" target="_blank">
                  <img class="img-blur-done" src="/static/image/10907624-107943365323e5b9.jpeg" />
                </a>
                <div class="content">
                  <a class="title" target="_blank" href="">“不耻下问”,正在毁掉你的人生</a>
                  <p class="abstract">
                    在过去,遇到不懂的问题,你不耻下问,找个人问问就行;在现在,如果你还这么干,多半会被认为是“搜商低”。 昨天,35岁的表姐把我拉黑了。 表姐是医...
                  </p>
                  <div class="meta">
                    <span class="jsd-meta">
                      <img src="/static/image/paid1.svg" alt=""> 6.7
                    </span>
                    <a class="nickname" target="_blank" href="">_飞鱼</a>
                    <a target="_blank" href="">
                      <img src="/static/image/comment.svg" alt=""> 33
                    </a>
                    <span><img src="/static/image/like.svg" alt=""> 113</span>
                    <span><img src="/static/image/shang.svg" alt=""> 2</span>
                  </div>
                </div>
              </li>
            </ul>
            <!-- 文章列表模块 -->
          </div>
        <a href="" class="load-more">阅读更多</a></div>
        <div class="aside">
          <!-- 推荐作者 -->
          <div class="recommended-author-wrap">
            <!---->
            <div class="recommended-authors">
              <div class="title">
                <span>推荐作者</span>
                <a class="page-change"><img class="icon-change" src="/static/image/exchange-rate.svg" alt="">换一批</a>
              </div>
              <ul class="list">
                <li>
                  <a href="" target="_blank" class="avatar">
                    <img src="/static/image/avatar.webp" />
                  </a>
                  <a class="follow" state="0"><img src="/static/image/follow.svg" alt="" />关注</a>
                  <a href="" target="_blank" class="name">董克平日记</a>
                  <p>写了807.1k字 · 2.5k喜欢</p>
                </li>
                <li>
                  <a href="" target="_blank" class="avatar">
                    <img src="/static/image/avatar.webp" />
                  </a>
                  <a class="follow" state="0"><img src="/static/image/follow.svg" alt="" />关注</a>
                  <a href="" target="_blank" class="name">董克平日记</a>
                  <p>写了807.1k字 · 2.5k喜欢</p>
                </li>

              </ul>
              <a href="" target="_blank" class="find-more">查看全部 ></a>
              <!---->
            </div>
          </div>
        </div>
      </div>
    </div>
    <Footer></Footer>
  </div>
</template>
<script>
  import Header from "./common/Header";
  import Footer from "./common/Footer";
  export default {
      name:"Home",
      data(){
          return {
            banner_list:[],
          }
      },
      components:{
        Header,
        Footer,
      },
      created(){
          this.get_banner();
      },
      methods:{
          get_banner(){
              this.$axios.get(`${this.$settings.Host}/banner/`).then(response=>{
                  this.banner_list = response.data;
              }).catch(error=>{
                  this.$message.error("网络异常!获取轮播图失败!");
              })
          }
      }
  }
</script>

<style>
.banner img{
  max-height: 100%;
  max-width: 100%;
}
</style>

导航功能实现

调整首页头部子组件的页面,Header.vue,效果:

<template>
  <div class="header">
    <nav class="navbar">
      <div class="width-limit">
        <!-- 左上方 Logo -->
        <a class="logo" href="/"><img src="/static/image/nav-logo.png" /></a>

        <!-- 右上角 -->
        <!-- 未登录显示登录/注册/写文章 -->
        <a class="btn write-btn" target="_blank" href="/writer"><img class="icon-write" src="/static/image/write.svg">写文章</a>
        <router-link class="btn sign-up" id="sign_up" to="/user/register">注册</router-link>
        <router-link class="btn log-in" id="sign_in" to="/user/login">登录</router-link>
        <div class="container">
          <div class="collapse navbar-collapse" id="menu">
            <ul class="nav navbar-nav">
              <li class="tab active">
                <a href="/">
                  <i class="iconfont ic-navigation-discover menu-icon"></i>
                  <span class="menu-text">首页</span>
                </a>
              </li>
              <li class="tab">
                <a href="/">
                  <i class="iconfont ic-navigation-follow menu-icon"></i>
                  <span class="menu-text">关注</span>
                </a>
                <ul class="dropdown-menu">
                  <li><a href=""><i class="iconfont ic-comments"></i> <span>评论</span></a></li>
                  <li><a href=""><i class="iconfont ic-chats"></i> <span>简信</span></a></li>
                  <li><a href=""><i class="iconfont ic-requests"></i> <span>投稿请求</span></a></li>
                  <li><a href=""><i class="iconfont ic-likes"></i> <span>喜欢和赞</span></a></li>
                  <li><a href=""><i class="iconfont ic-follows"></i> <span>关注</span></a></li>
                  <li><a href=""><i class="iconfont ic-money"></i> <span>赞赏和付费</span></a></li>
                  <li><a href=""><i class="iconfont ic-others"></i> <span>其它提醒</span></a></li>
                </ul>
              </li>
              <li class="tab">
                <a href="/">
                  <i class="iconfont ic-navigation-notification menu-icon"></i>
                  <span class="menu-text">消息</span>
                </a>
              </li>
              <li class="search">
                <form target="_blank" action="/search" accept-charset="UTF-8" method="get">
                  <input type="text" name="q" id="q" value="" autocomplete="off" placeholder="搜索" class="search-input">
                  <a class="search-btn" href="javascript:void(0)"></a>
                </form>
              </li>
            </ul>
          </div>
        </div>

        <!-- 如果用户登录,显示下拉菜单 -->
      </div>
    </nav>
  </div>
</template>

<script>
    export default {
        name: "Header"
    }
</script>

<style scoped>
.header{
  height: 56px;
}
.container {
    width: 960px;
    margin-right: auto;
    margin-left: auto;
    padding-left: 15px;
    padding-right: 15px;
}
.container:after, .container:before {
    content: " ";
    display: table;
}
.container:after {
    clear: both;
}
.navbar {
    background-color: #fff;
    border-color: #f0f0f0;
    top: 0;
    border-width: 0 0 1px;
    border-radius: 0;
}
.navbar-nav {
    float: left;
    margin: 0;
}
.navbar:after, .navbar:before {
    content: " ";
    display: table;
    box-sizing: border-box;
}
.nav:after, .nav:before {
    content: " ";
    display: table;
}
nav .width-limit {
    min-width: 768px;
    max-width: 1440px;
    margin: 0 auto;
}
nav .logo {
    float: left;
    height: 56px;
    padding: 0;
}
nav .logo img {
    height: 100%;
    vertical-align: middle;
    border: 0;
}
.btn {
    display: inline-block;
    margin-bottom: 0;
    font-weight: 400;
    text-align: center;
    vertical-align: middle;
    touch-action: manipulation;
    cursor: pointer;
    background-image: none;
    border: 1px solid transparent;
    white-space: nowrap;
    padding: 6px 12px;
    font-size: 14px;
    line-height: 1.42857;
    border-radius: 4px;
}
nav .write-btn {
    float: right;
    width: 100px;
    height: 24px;
    line-height: 24px;
    margin: 8px 12px 0;
    border-radius: 20px;
    font-size: 15px;
    color: #fff;
    background-color: #ea6f5a;
    text-decoration: none;
}
nav .log-in, nav .log-in:hover {
    color: #969696;
}
nav .log-in {
    float: right;
    margin: 11px 6px 0 10px;
    font-size: 15px;
}
nav .sign-up {
    float: right;
    width: 80px;
    height: 24px;
    line-height: 24px;
    margin: 9px 5px 0 15px;
    border: 1px solid rgba(236,97,73,.7);
    border-radius: 20px;
    font-size: 15px;
    color: #ea6f5a;
    background-color: transparent;
}
nav .icon-write {
    margin-right: 3px;
    width: 19px;
    height: 19px;
    vertical-align: middle;
}
nav .menu-text{
    font-size: 17px;
}
nav .active a{
  color: #ea6f5a;
}
nav .menu-icon {
    width: 20px;
    height: 20px;
    vertical-align: baseline;
    margin-right: 3px;
}
.tab:hover .dropdown-menu{
  display: block;
}
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;
  float:left;min-width:120px;padding:5px 0;margin:2px 0 0;list-style:none;
  font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;
  border:1px solid rgba(0,0,0,.15);border-radius:4px;
  box-shadow:0 6px 12px rgba(0,0,0,.175);
  background-clip:padding-box}
.dropdown-menu.pull-right{right:0;left:auto}
.dropdown-menu .divider{
  height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}
.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;
  line-height:1.42857;color:#333;white-space:nowrap}
.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{
  text-decoration:none;color:#262626;background-color:#f5f5f5}
.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{
  color:#fff;text-decoration:none;outline:0;background-color:#337ab7}
.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{
  color:#777
}
.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{
  text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}
.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}
.navbar-right .dropdown-menu-left{left:0;right:auto}
.dropdown-menu{
  width:150px;margin-top:-1px;border-radius:0 0 4px 4px
}
.dropdown-menu li{margin:0}
.dropdown-menu a{height:auto;padding:10px 20px;line-height:30px}
.dropdown-menu a:hover{background-color:#f5f5f5}
.dropdown-menu i{margin-right:15px;font-size:22px;color:#ea6f5a;
  vertical-align:middle
}
.dropdown-menu span{vertical-align:middle}
.dropdown-menu .badge{position:absolute;right:15px;margin-top:7px}

nav .nav .tab a {
    height: 56px;
    line-height: 26px;
    padding: 15px;
    background: none;
}
nav .navbar-nav li {
    margin-right: 10px;
    float: left;
    position: relative;
    display: block;
    box-sizing: border-box;
    height: 56px;
    line-height: 56px;
}
.navbar-nav {
    float: left;
    margin: 0;
}
nav form {
    position: relative;
    top: 9px;
    margin: 0 0 20px;
    box-sizing: border-box;
    line-height: 20px;
}
nav form .search-input {
    padding: 0 40px 0 20px;
    height: 38px;
    font-size: 14px;
    border: 1px solid #eee;
    border-radius: 40px;
    background: #eee;
    transition: width .5s;
    width: 240px;
    outline: none;
}
nav form .search-input:focus {
    width: 320px;
    outline: none;
}
.navbar-default .navbar-collapse, .navbar-default .navbar-form {
    border-color: #e7e7e7;
    padding-left: 0;
    padding-right: 0;
    box-sizing: border-box;
    width: auto;
    border-top: 0;
    box-shadow: none;
}
.navbar {
    background-color: #fff;
    top: 0;
    border-radius: 0;
    position: fixed;
    right: 0;
    left: 0;
    z-index: 1030;
    min-height: 50px;
    margin-bottom: 20px;
    border-bottom: 1px solid #f0f0f0;
}
nav {
    height: 56px;
}
.navbar:after, .navbar:before {
    content: " ";
    display: table;
}
nav form .search-btn {
    position: absolute;
    display: block;
    top: 0;
    right: 10px;
    width: 30px;
    height: 30px;
    padding: 0;
    margin: 5px -1px 0 0;
    background: transparent url("../../../static/image/search-focus.svg") no-repeat 6px 6px;
    background-size: 20px;
}
nav form .search-input:focus~a{
    border-radius: 50%;
    background-color: #696969;
    background-image: url("../../../static/image/search-blur.svg");
}
nav .sign-up:hover {
    color: #ec6149;
    border-color: #ec6149;
    background-color: rgba(236,97,73,.05);
}
nav .write-btn:focus, nav .write-btn:hover {
    color: #fff;
    background-color: #ec6149;
}
</style>

创建模型

idnamepid
1消息0
2广场0
3评论1
4留言1

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

from django.db import models
from renranapi.utils.models import BaseModel
# Create your models here.
class Banner(BaseModel):
    """
    轮播图
    """
    # upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to
    image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True,blank=True)
    note = models.CharField(null=True, blank=True, max_length=150, verbose_name='备注信息')
    link = models.CharField(null=True, blank=True, max_length=150, verbose_name='轮播图广告地址', help_text="站内地址格式:/users/<br>站外地址格式:http://www.baidu.com")
    start_time = models.DateTimeField(verbose_name="上架时间",default=None, null=True, blank=True)
    end_time = models.DateTimeField(verbose_name="下架时间",default=None, null=True, blank=True)
    is_http = models.BooleanField(verbose_name="是否站外地址", default=False)

    class Meta:
        db_table = 'rr_banner'
        verbose_name = '轮播图'
        verbose_name_plural = verbose_name

class Nav(BaseModel):
    """导航菜单"""
    POSITION = (
        (1, "头部导航"),
        (2, "脚部导航"),
    )
    pid = models.ForeignKey("Nav", related_name="son", null=True, blank=True, on_delete=models.DO_NOTHING, verbose_name="父亲导航", )
    link = models.CharField(max_length=500, verbose_name='导航地址', help_text="如果是站外链接,必须加上协议, 格式如: http://www.renran.cn")
    is_http = models.BooleanField(verbose_name="是否站外地址", default=False)
    option = models.SmallIntegerField(choices=POSITION, default=1, verbose_name="导航位置")

    class Meta:
        db_table = 'rr_nav'
        verbose_name = '导航菜单'

    @property
    def son_list(self):
        """子导航列表"""
        result = self.son.filter(is_show=True,is_delete=False).order_by("orders","-id")[:8]
        data = []
        for nav in result:
            data.append({
                "name": nav.name,
                "link": nav.link,
                "is_http": nav.is_http,
            })
        return data

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

from django.db import models
class BaseModel(models.Model):
    """公共模型"""
    name = models.CharField(null=True, blank=True, max_length=150, verbose_name='名称')
    orders = models.IntegerField(default=0, verbose_name='显示顺序')
    is_show = models.BooleanField(default=False, verbose_name="是否上架")
    is_delete = models.BooleanField(default=False, verbose_name="逻辑删除")
    created_time = models.DateTimeField(null=True,blank=True, auto_now_add=True, verbose_name="添加时间")
    updated_time = models.DateTimeField(null=True,blank=True, auto_now=True, verbose_name="更新时间")

    class Meta:
        # 设置当前模型在数据迁移的时候不要为它创建表
        abstract = True

    def __str__(self):
        return self.name

数据迁移

python manage.py makemigrations
python manage.py migrate

注册导航模型到xadmin中

在home子应用adminx.py,添加如下代码

# 导航
from .models import Nav
class NavModelAdmin(object):
    list_display=["name","link","is_http"]
xadmin.site.register(Nav, NavModelAdmin)

添加测试数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JJI1isp0-1595161631881)(E:/Python全栈8-26/荏苒/day020/assets/1557462133673.png)]

序列化器代码

home/serializers.py,代码:

from .models import Nav
class NavModelSerializer(serializers.ModelSerializer):
    """导航菜单序列化器"""
    class Meta:
        model = Nav
        fields = ["name","link","is_http","son_list"]

视图代码

home/views.py

from .models import Nav
from .serializers import NavModelSerializer
class NavHeaderListAPIView(ListAPIView):
    queryset = Nav.objects.filter(is_show=True, is_delete=False, option=1,pid=None).order_by("orders","-id")[:constants.HEADER_NAV_LENGTH]
    serializer_class = NavModelSerializer

class NavFooterListAPIView(ListAPIView):
    queryset = Nav.objects.filter(is_show=True, is_delete=False, option=2,pid=None).order_by("orders","-id")[:constants.FOOTER_NAV_LENGTH]
    serializer_class = NavModelSerializer

settings/constants.py,代码:

# 头部顶级导航数量
HEADER_NAV_LENGTH = 8
# 脚部顶级导航数量
FOOTER_NAV_LENGTH = 8

路由代码

urls.py

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

客户端获取导航数据

Header.vue代码:

<template>
  <div class="header">
    <nav class="navbar">
      <div class="width-limit">
        <!-- 左上方 Logo -->
        <a class="logo" href="/"><img src="/static/image/nav-logo.png" /></a>

        <!-- 右上角 -->
        <!-- 未登录显示登录/注册/写文章 -->
        <a class="btn write-btn" target="_blank" href="/writer"><img class="icon-write" src="/static/image/write.svg">写文章</a>
        <router-link class="btn sign-up" id="sign_up" to="/user/register">注册</router-link>
        <router-link class="btn log-in" id="sign_in" to="/user/login">登录</router-link>
        <div class="container">
          <div class="collapse navbar-collapse" id="menu">
            <ul class="nav navbar-nav">
              <li class="tab active">
                <a href="/">
                  <i class="iconfont ic-navigation-discover menu-icon"></i>
                  <span class="menu-text">首页</span>
                </a>
              </li>
              <li class="tab" v-for="nav in nav_list">
                <a :href="nav.link" v-if="nav.is_http">
                  <i class="iconfont ic-navigation-follow menu-icon"></i>
                  <span class="menu-text">{{nav.name}}</span>
                </a>
                <router-link :to="nav.link" v-else>
                  <i class="iconfont ic-navigation-follow menu-icon"></i>
                  <span class="menu-text">{{nav.name}}</span>
                </router-link>
                <ul class="dropdown-menu" v-if="nav.son_list.length>0">
                  <li v-for="son in nav.son_list">
                    <a :href="son.link" v-if="son.is_http" target="_blank"><i class="iconfont ic-comments"></i> <span>{{son.name}}</span></a>
                    <router-link :to="son.link" v-else><i class="iconfont ic-comments"></i> <span>{{son.name}}</span></router-link>
                  </li>
                </ul>
              </li>
              <li class="search">
                <form target="_blank" action="/search" accept-charset="UTF-8" method="get">
                  <input type="text" name="q" id="q" value="" autocomplete="off" placeholder="搜索" class="search-input">
                  <a class="search-btn" href="javascript:void(0)"></a>
                </form>
              </li>
            </ul>
          </div>
        </div>

        <!-- 如果用户登录,显示下拉菜单 -->
      </div>
    </nav>
  </div>
</template>

<script>
    export default {
        name: "Header",
        data(){
            return {
                nav_list:[
                    {
                        son_list:[]
                    }
                ],
            }
        },
        created(){
            this.get_nav();
        },
        methods:{
            get_nav(){
                this.$axios.get(`${this.$settings.Host}/nav/header/`).then(response=>{
                    this.nav_list = response.data;
                }).catch(error=>{
                    this.$message.error("网络异常,无法获取头部导航信息!");
                })
            }
        }
    }
</script>

Footer.vue

<template>
<footer class="container">
  <div class="row">
    <div class="main">
      <span :key="key" v-for="nav,key in nav_list">
        <a :href="nav.link" target="_blank"  v-if="nav.is_http">{{nav.name}}</a>
        <router-link :to="nav.link" v-else>{{nav.name}}</router-link>
        <em> · </em>
      </span>
      <div class="icp">©2016-2019 广州荏苒信息科技有限公司 / 荏苒 / 粤ICP备16018329号-5 /</div>
    </div>
  </div>
</footer>
</template>

<script>
    export default {
        name: "Footer",
        data(){
            return {
                nav_list:[],
            }
        },
        created(){
            this.get_nav();
        },
        methods:{
            get_nav(){
                this.$axios.get(`${this.$settings.Host}/nav/footer/`).then(response=>{
                    this.nav_list = response.data;
                }).catch(error=>{
                    this.$message.error("无法获取脚步导航信息");
                })
            }
        }
    }
</script>

ipt>


Footer.vue

```vue
<template>
<footer class="container">
  <div class="row">
    <div class="main">
      <span :key="key" v-for="nav,key in nav_list">
        <a :href="nav.link" target="_blank"  v-if="nav.is_http">{{nav.name}}</a>
        <router-link :to="nav.link" v-else>{{nav.name}}</router-link>
        <em> · </em>
      </span>
      <div class="icp">©2016-2019 广州荏苒信息科技有限公司 / 荏苒 / 粤ICP备16018329号-5 /</div>
    </div>
  </div>
</footer>
</template>

<script>
    export default {
        name: "Footer",
        data(){
            return {
                nav_list:[],
            }
        },
        created(){
            this.get_nav();
        },
        methods:{
            get_nav(){
                this.$axios.get(`${this.$settings.Host}/nav/footer/`).then(response=>{
                    this.nav_list = response.data;
                }).catch(error=>{
                    this.$message.error("无法获取脚步导航信息");
                })
            }
        }
    }
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值