luffcc项目-02-轮播图功能实现、xadmin配置、导航功能实现、Git提交

一、轮播图功能实现

1.安装依赖模块和配置

图片处理模块

	pip install pillow

上传文件相关配置

settings.py,由于我们需要在后台上传我们的轮播图图片,所以我们需要在django中配置一下上传文件的相关配置,有了它之后,就不需要我们自己写上传文件,保存文件的操作了,看配置:

# 访问静态文件的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/"

在django项目中转换上传文件的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}),
]
2.注册home子应用

因为当前功能是drf的第一个功能,所以我们先创建一个子应用home,创建在lufapi/apps目录下:E:\axiangmu\luffcc\lufapi\lufapi\apps>python …/…/manage.py startapp home
注册home子应用,因为子应用的位置发生了改变,所以为了原来子应用的注册写法,所以新增一个导包路径:

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 新增一个系统导包路径
import sys
#sys.path使我们可以直接import导入时使用到的路径,所以我们直接将我们的apps路径加到默认搜索路径里面去,那么django就能直接找到apps下面的应用了
sys.path.insert(0,os.path.join(BASE_DIR,"apps"))



INSTALLED_APPS = [
	# 注意,加上drf框架的注册	
    'rest_framework',
    
    # 子应用
    'home',

]

新建开发分支进行独立开发

# 新建一个分支 
git branch 分支名称

# 查看所有分支
git branch

# 切换分支[-b表示新建分支的同时并切换到新分支]
git checkout -b 分支名称

# 删除分支
git branch -d 分支名称

接下来,我们可以创建一个dev开发分支并在开发分支下:

git branch dev
git checkout dev
3.创建轮播图的模型

home/models.py

from django.db import models

# Create your models here.
class Banner(models.Model):
    """轮播广告图模型"""
    # 模型字段
    title = models.CharField(max_length=500, verbose_name="广告标题")
    link = models.CharField(max_length=500, verbose_name="广告链接")
    # upload_to 设置上传文件的保存子目录,将来上传来的文件会存到我们的media下面的banner文件夹下,这里存的是图片地址。
    image_url =  models.ImageField(upload_to="banner", null=True, blank=True, max_length=255, verbose_name="广告图片")
    remark = models.TextField(verbose_name="备注信息")
    is_show = models.BooleanField(default=False, verbose_name="是否显示") #将来轮播图肯定会更新,到底显示哪些
    orders = models.IntegerField(default=1, verbose_name="排序")
    is_deleted = models.BooleanField(default=False, verbose_name="是否删除")

    # 表信息声明
    class Meta:
        db_table = "ly_banner"
        verbose_name = "轮播广告"
        verbose_name_plural = verbose_name

    # 自定义方法[自定义字段或者自定义工具方法]
    def __str__(self):
        return self.title

数据迁移

python manage.py makemigrations
python manage.py migrate
4.序列化器

home/serializers.py

from rest_framework import serializers
from . import models
class BannerModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Banner
        fields = ['id', 'image_url', 'link']
5.视图代码

views.py

from django.shortcuts import render

# Create your views here.
from rest_framework.generics import ListAPIView
from lufapi.settings import contains
from . import models
from .serializers import BannerModelSerializer


class BannerView(ListAPIView):
    queryset = models.Banner.objects.filter(is_show=True, is_deleted=False)[0:contains.BANNER_LENGTH]
    serializer_class = BannerModelSerializer

在settings配置文件夹中创建一个contains.py配置文件,将来里面存放我们所有的一些常量信息配置,比如上面的轮播图数据切片长度

BANNER_LENGTH = 3
6.路由代码

home/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('banner/', views.BannerView.as_view()),
]

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

from django.contrib import admin
from django.urls import path,re_path,include
from django.conf import settings
from django.views.static import serve

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

二、xadmin配置

1.基本配置

需要有一个后台提供数据.安装xadmin

pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2 -i https://pypi.douban.com/simple/

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

INSTALLED_APPS = [
    ...
    'xadmin',
    'crispy_forms',
    'reversion',
    ...
]

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

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

xadmin有建立自己的数据库模型类,需要进行数据库迁移

python manage.py makemigrations
python manage.py migrate

在总路由中添加xadmin的路由信息

import xadmin
xadmin.autodiscover()

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

urlpatterns = [
    path(r'xadmin/', xadmin.site.urls),
]

如果之前没有创建超级用户,需要创建,如果有了,则可以直接使用之前的。

python manage.py createsuperuser
2.给xadmin设置基本站点配置信息

在当前子应用中创建adminx.py:

import xadmin
from xadmin import views


class BaseSetting(object):
    """xadmin的基本配置"""
    enable_themes = True  # 开启主题切换功能
    use_bootswatch = True


xadmin.site.register(views.BaseAdminView, BaseSetting)


class GlobalSettings(object):
    """xadmin的全局配置"""
    site_title = "31期学城"  # 设置站点标题
    site_footer = "31期学城有限公司"  # 设置站点的页脚
    menu_style = "accordion"  # 设置菜单折叠


xadmin.site.register(views.CommAdminView, GlobalSettings)
3.注册轮播图模型到xadmin中

在当前子应用中创建adminx.py,添加如下代码

# 轮播图
from . import models


class BannerXAdmin(object):
    list_display = ['id',  'title', 'link','image_url']
    search_fields = ['id', 'title']


xadmin.site.register(models.Banner, BannerXAdmin)
4.修改后端xadmin中子应用名称

子应用/apps.py

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

在home这个app下面的__init__.py中设置下面的内容

default_app_config = "home.apps.HomeConfig"

下面给轮播图添加测试数据,如图:

postman测试图:

5.客户端代码获取数据

在前端lufcity的Banner.vue中的代码:

<template>
    <el-carousel indicator-position="outside" height="400px">
      <el-carousel-item v-for="(value,index) in banner_list" :key="value.id">
        <a :href="value.link">
          <!-- router-link 站内跳转 -->
          <!-- a标签 站外跳转 -->
          <img :src="value.imag_url" alt="" style="width: 100%;height: 400px;">
<!--          <img src="@/assets/banner1.png" alt="">-->
        </a>
      </el-carousel-item>
    </el-carousel>



</template>

<script>
export default {
  name: "Banner",
  data(){
    return {
      banner_list:[

      ]
    }
  },
  methods:{
    get_banner_data(){
      this.$axios.get(`${this.$settings.Host}/home/banner`)
        .then((res)=>{
          console.log(res);
          this.banner_list = res.data;
          }
        )
      .catch((error)=>{

        }
      )
    }
  },
  created(){
      this.get_banner_data();
  },

}
</script>

<style scoped>

</style>

在开发之前应该将分支切换至dev,现在进行提交代码

	git add .
	git commit -m 'v1轮播图提交'
	git checkout master
	git merge dev
	git add .
	git commit -m 'v2master轮播图提交'
	git push origin master

	git remote add origin https://gitee.com/cui_wenjun/study.git
	git push -u origin master

三、导航功能实现

1.创建模型

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

from django.db import models
from lufapi.utils.models import BaseModel

# Create your models here.
class Banner(models.Model):
    """轮播广告图模型"""
    # 模型字段
    title = models.CharField(max_length=500, verbose_name="广告标题")
    link = models.CharField(max_length=500, verbose_name="广告链接")
    # upload_to 设置上传文件的保存子目录,将来上传来的文件会存到我们的media下面的banner文件夹下,这里存的是图片地址。
    image_url =  models.ImageField(upload_to="banner", null=True, blank=True, max_length=255, verbose_name="广告图片")
    remark = models.TextField(verbose_name="备注信息")
    is_show = models.BooleanField(default=False, verbose_name="是否显示") #将来轮播图肯定会更新,到底显示哪些
    orders = models.IntegerField(default=1, verbose_name="排序")
    is_deleted = models.BooleanField(default=False, verbose_name="是否删除")

    # 表信息声明
    class Meta:
        db_table = "ly_banner"
        verbose_name = "轮播广告"
        verbose_name_plural = verbose_name

    # 自定义方法[自定义字段或者自定义工具方法]
    def __str__(self):
        return self.title



class Nav(BaseModel):
    """导航菜单模型"""
    POSITION_OPTION = (
        (1, "顶部导航"),
        (2, "脚部导航"),
    )
    title = models.CharField(max_length=500, verbose_name="导航标题")
    link = models.CharField(max_length=500, verbose_name="导航链接")
    position = models.IntegerField(choices=POSITION_OPTION, default=1, verbose_name="导航位置")
    is_site = models.BooleanField(default=False, verbose_name="是否是站外地址")

    class Meta:
        db_table = 'ly_nav'
        verbose_name = '导航菜单'
        verbose_name_plural = verbose_name

    # 自定义方法[自定义字段或者自定义工具方法]
    def __str__(self):
        return self.title

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


from django.db import models
class BaseModel(models.Model):
    """公共模型"""
    is_show = models.BooleanField(default=False, verbose_name="是否显示")
    orders = models.IntegerField(default=1, verbose_name="排序")
    is_deleted = models.BooleanField(default=False, verbose_name="是否删除")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
    updated_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    class Meta:
        abstract = True  #执行数据库同步指令是,不会生成表

数据迁移

python manage.py makemigrations
python manage.py migrate
2.序列化器代码

在上面的序列化器中:


class NavModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Nav
        fields = ['id', 'title', 'link', 'is_site']
3.视图代码

view.py

from .serializers import BannerModelSerializer,NavModelSerializer

class NavView(ListAPIView):

    queryset = models.Nav.objects.filter(is_deleted=False, is_show=True, position=1)[0:contains.NAV_TOP_LENGH]
    serializer_class = NavModelSerializer
4.常量配置

settings/contains.py

BANNER_LENGTH = 3

NAV_TOP_LENGH = 5

NAV_BOTTOM_LENGH = 5
5.路由代码

urls.py

urlpatterns = [
    path('banner/', views.BannerView.as_view()),
    path('nav/top/', views.NavView.as_view()),
    # path(r'nav/bottom/', views.BottomView.as_view(),),

]
6.注册导航模型到xadmin中

在当前子应用adminx.py,添加如下代码:

class NavXAdmin(object):
    list_display = ['id',  'title', 'link']
    search_fields = ['id', 'title']


xadmin.site.register(models.Nav, NavXAdmin)

添加测试数据

7.客户端代码获取数据

VHeader.vue代码:

<template>
  <div class="total-header">
    <div class="header">
      <el-container>
        <el-header height="80px" class="header-cont">
          <el-row>
            <el-col class="logo" :span="3">
              <a href="/">
                <img src="@/assets/header-logo.svg" alt="">

              </a>
            </el-col>
            <el-col class="nav" :span="10">
              <el-row>
                <el-col :span="3" v-for="(value,index) in nav_data_list" :key="index">

                  <a :href="value.link" class="active" v-if="value.is_site">{{value.title}}</a>

                  <router-link :to="value.link" v-else>{{value.title}}</router-link>

                </el-col>

              </el-row>

            </el-col>
            <el-col :span="11" class="header-right-box">
              <div class="search">
                <input type="text" id="Input" placeholder="请输入想搜索的课程" style="" @blur="inputShowHandler" ref="Input"
                       v-show="!s_status">
                <ul @click="ulShowHandler" v-show="s_status" class="search-ul" ref="xx">
                  <span>Python</span>
                  <span>Linux</span>
                </ul>
                <p>
                  <img class="icon" src="@/assets/sousuo1.png" alt="" v-show="s_status">
                  <img class="icon" src="@/assets/sousuo2.png" alt="" v-show="!s_status">
                  <img class="new" src="@/assets/new.png" alt="">
                </p>
              </div>
              <div class="register" v-show="!token">
                <router-link to="/user/login">
                  <button class="signin">登录</button>
                </router-link>
                &nbsp;&nbsp;|&nbsp;&nbsp;
                <a target="_blank" href="https://www.luffycity.com/signup">
                  <router-link to="/register">
                    <button class="signup">注册</button>
                  </router-link>

                </a>
              </div>
              <div class="shop-car" v-show="token">
                <router-link to="/cart">
                  <b>6</b>
                  <img src="@/assets/shopcart.png" alt="">
                  <span>购物车 </span>
                </router-link>
              </div>
              <div class="nav-right-box" v-show="token">
                <div class="nav-right">
                  <router-link to="/myclass">
                    <div class="nav-study">我的教室</div>
                  </router-link>
                  <div class="nav-img" @mouseover="personInfoList" @mouseout="personInfoOut">
                    <img src="@/assets/touxiang.png" alt="" style="border: 1px solid rgb(243, 243, 243);">
                    <ul class="home-my-account" v-show="list_status" @mouseover="personInfoList">

                      <li>
                        我的账户
                        <img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
                             alt="">
                      </li>
                      <li>
                        我的订单
                        <img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
                             alt="">
                      </li>
                      <li>
                        贝里小卖铺
                        <img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
                             alt="">
                      </li>
                      <li>
                        我的优惠券
                        <img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
                             alt="">
                      </li>
                      <li>
                    <span>
                      我的消息
                      <b>(26)</b>
                    </span>
                        <img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
                             alt="">
                      </li>
                      <li @click="logout">
                        退出
                        <img src="https://hcdn2.luffycity.com/media/frontend/activity/back_1568185800.821227.svg"
                             alt="">
                      </li>

                    </ul>
                  </div>

                </div>

              </div>


            </el-col>
          </el-row>

        </el-header>


      </el-container>

    </div>
  </div>

</template>

<script>
export default {
  name: "Vheader",
  data() {
    return {

      token: false, // 登录成功与否的标记
      s_status: true, // 放大镜效果切换控制,默认input标签不显示
      list_status: false, // 个人中心下拉菜单是否显示

      nav_data_list:[],
    }
  },
  created(){
    this.get_nav_data();
    this.check_login();
  },
  methods: {
    ulShowHandler() {
      // console.log(this);
      this.s_status = false;
      console.log(this.$refs);

      this.$nextTick(function () {
        console.log(this);
        this.$refs.Input.focus();
      });


    },
    inputShowHandler() {
      console.log('xxxxx')
      this.s_status = true;
    },
    personInfoList() {
      this.list_status = true;
    },
    personInfoOut() {
      this.list_status = false;
    },

    get_nav_data(){
      this.$axios.get(`${this.$settings.Host}/home/nav/top/`)
      .then((res)=>{
        this.nav_data_list = res.data;
      })
    },

    check_login(){
      this.token = localStorage.token || sessionStorage.token;
      //console.log(this.token);
    },
    // 退出登录
    logout(){

      sessionStorage.removeItem('token');
      sessionStorage.removeItem('username');
      sessionStorage.removeItem('id');
      localStorage.removeItem('token');
      localStorage.removeItem('username');
      localStorage.removeItem('id');
      console.log('xxxxxx')
      this.check_login();
      // this.token = false;
    }

  }
}


</script>

<style scoped>
.header-cont .nav .active {
  color: #f5a623;
  font-weight: 500;
  border-bottom: 2px solid #f5a623;
}

.total-header {
  min-width: 1200px;
  z-index: 100;
  box-shadow: 0 4px 8px 0 hsla(0, 0%, 59%, .1);
}

.header {
  width: 1200px;
  margin: 0 auto;
}

.header .el-header {
  padding: 0;
}

.logo {
  height: 80px;
  /*line-height: 80px;*/
  /*text-align: center;*/
  display: flex; /* css3里面的弹性布局,高度设定好之后,设置这个属性就能让里面的内容居中 */
  align-items: center;
}

.nav .el-row .el-col {
  height: 80px;
  line-height: 80px;
  text-align: center;

}

.nav a {
  font-size: 15px;
  font-weight: 400;
  cursor: pointer;
  color: #4a4a4a;
  text-decoration: none;
}

.nav .el-row .el-col a:hover {
  border-bottom: 2px solid #f5a623
}

.header-cont {
  position: relative;
}

.search input {
  width: 185px;
  height: 26px;
  font-size: 14px;
  color: #4a4a4a;
  border: none;
  border-bottom: 1px solid #ffc210;

  outline: none;
}

.search ul {
  width: 185px;
  height: 26px;
  display: flex;
  align-items: center;
  padding: 0;

  padding-bottom: 3px;
  border-bottom: 1px solid hsla(0, 0%, 59%, .25);
  cursor: text;
  margin: 0;
  font-family: Helvetica Neue, Helvetica, Microsoft YaHei, Arial, sans-serif;
}

.search .search-ul, .search #Input {
  padding-top: 10px;
}

.search ul span {
  color: #545c63;
  font-size: 12px;
  padding: 3px 12px;
  background: #eeeeef;
  cursor: pointer;
  margin-right: 3px;
  border-radius: 11px;
}

.hide {
  display: none;
}

.search {
  height: auto;
  display: flex;
}

.search p {
  position: relative;
  margin-right: 20px;
  margin-left: 4px;
}

.search p .icon {
  width: 16px;
  height: 16px;
  cursor: pointer;
}

.search p .new {
  width: 18px;
  height: 10px;
  position: absolute;
  left: 15px;
  top: 0;
}

.register {
  height: 36px;
  display: flex;
  align-items: center;
  line-height: 36px;
}

.register .signin, .register .signup {
  font-size: 14px;
  color: #5e5e5e;
  white-space: nowrap;
}

.register button {
  outline: none;
  cursor: pointer;
  border: none;
  background: transparent;
}

.register a {
  color: #000;
  outline: none;
}

.header-right-box {
  height: 100%;
  display: flex;
  align-items: center;
  font-size: 15px;
  color: #4a4a4a;
  position: absolute;
  right: 0;
  top: 0;
}

.shop-car {
  width: 99px;
  height: 28px;
  border-radius: 15px;
  margin-right: 20px;
  background: #f7f7f7;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  cursor: pointer;
}

.shop-car b {
  position: absolute;
  left: 28px;
  top: -1px;
  width: 18px;
  height: 16px;
  color: #fff;
  font-size: 12px;
  font-weight: 350;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  background: #ff0826;
  overflow: hidden;
  transform: scale(.8);
}

.shop-car img {
  width: 20px;
  height: 20px;
  margin-right: 7px;
}

.nav-right-box {
  position: relative;
}

.nav-right-box .nav-right {
  float: right;
  display: flex;
  height: 100%;
  line-height: 60px;
  position: relative;
}

.nav-right .nav-study {
  font-size: 15px;
  font-weight: 300;
  color: #5e5e5e;
  margin-right: 20px;
  cursor: pointer;

}

.nav-right .nav-study:hover {
  color: #000;
}

.nav-img img {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: inline-block;
  cursor: pointer;
}

.home-my-account {
  position: absolute;
  right: 0;
  top: 60px;
  z-index: 101;
  width: 190px;
  height: auto;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 4px 8px 0 #d0d0d0;
}

li {
  list-style: none;
}

.home-my-account li {
  height: 40px;
  font-size: 14px;
  font-weight: 300;
  color: #5e5e5e;
  padding-left: 20px;
  padding-right: 20px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
}

.home-my-account li img {
  cursor: pointer;
  width: 5px;
  height: 10px;
}

.home-my-account li span {
  height: 40px;
  display: flex;
  align-items: center;
}

.home-my-account li span b {
  font-weight: 300;
  margin-top: -2px;
}


</style>


底部导航待补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值