路飞学城搭建值前后端结合
一.首页搭建
1.轮播图
pip install Pillow (这是在后端操作的,在之前搭建的后端环境中直接安装这个模块)
默认情况下,Django会将上传的图片保存在本地服务器上,需要配置保存的路径。
我们可以将上传的文件保存在静态文件statics目录中,添加如下上传保存目录信息
# 访问静态文件的url地址前缀 STATIC_URL = '/static/' # 项目中存储上传文件的根目录[暂时配置],注意,static目录需要手动创建否则上传文件时报错 MEDIA_ROOT=os.path.join(BASE_DIR,"luffy/static") # 设置django的静态文件目录 STATICFILES_DIRS = [ os.path.join(BASE_DIR,"luffy/static") ]
把Home子应用注册到settings的INSTALLED_APPS中,并在models.py里面创建模型。
INSTALLED_APPS = [ ... 'home', ]![]()
home的views.py
使用图片字段ImageFiled
,字段选项使用upload_to
可以设置保存图片的子目录,数据模型代码:
class bannerInfo(models.Model): """ 轮播图 """ # upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True) name = models.CharField(max_length=150, verbose_name='轮播图名称') link = models.CharField(max_length=150, verbose_name='轮播图广告地址') orders = models.IntegerField(verbose_name='显示顺序') is_show=models.BooleanField(verbose_name="是否上架",default=False) is_delete=models.BooleanField(verbose_name="逻辑删除",default=False) class Meta: db_table = 'ly_banner' verbose_name = '轮播图' verbose_name_plural = verbose_name def __str__(self): return self.name
模型数据迁移:
python manage.py makemigrations
python manage.py migrate
在xadmin站点添加轮播图数据
pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
在配置文件中注册如下应用:
INSTALLED_APPS = [ ... 'rest_framework', 'xadmin', 'crispy_forms', 'reversion', ... ] # 修改使用中文界面 LANGUAGE_CODE = 'zh-Hans' # 修改时区 TIME_ZONE = 'Asia/Shanghai'
xadmin有建立自己的数据库模型类,需要进行数据库迁移
python manage.py makemigrations
python manage.py migrate
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
luffy/home/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 = "路飞学城" # 设置站点标题 site_footer = "路飞学城有限公司" # 设置站点的页脚 menu_style = "accordion" # 设置菜单折叠 xadmin.site.register(views.CommAdminView, GlobalSettings) # 轮播图 from .models import bannerInfo class BannerInfoModelAdmin(object): list_display=["name","orders","is_show"] xadmin.site.register(bannerInfo, BannerInfoModelAdmin)
视图:
luffy/home/views.py
from django.shortcuts import render # Create your views here. from django.db.models import Q from rest_framework.views import APIView from rest_framework.response import Response from .models import bannerInfo class BannerInfoAPIView(APIView): """ 轮播图列表 """ def get(self,request): # 获取数据 banners = bannerInfo.objects.filter(Q(is_show=True) & Q(is_delete=False)).order_by("-orders") # 调整banners的images字段 # 序列化 data = [] for item in banners: data.append({ # 拼接图片的url地址 "image": "/static/" + item.image.url, "link":item.link, "orders":item.orders, }) return Response(data)
路由代码:
子应用路由:luffy/home/urls.py
from django.urls import path,re_path from . import views urlpatterns = [ path(r"banner/",views.BannerInfoAPIView.as_view()), ]
总路由:
urlpatterns = [ ... path('home/', include("home.urls")), # include 的值必须是 模块名.urls 格式,字符串中间只能出现一个圆点 ]
2.前端代码(去admin那边获取api数据的)
<template> <div class="banner"> <el-carousel trigger="click" height="506px"> <el-carousel-item v-for="item in banner_list"> <a :href="item.link"><img :src="item.image"></a> </el-carousel-item> </el-carousel> </div> </template> <script> export default { name:"Banner", data(){ return { banner_list:[], }; }, created: function(){ // 获取轮播图 this.$axios.get("http://api.luffycity.cn:8000/home/banner/").then(res => { this.banner_list = res.data }).catch(error => { console.log(error); }); } } </script> <style scoped> .banner img{ width: 100%; } </style>
3.前端去后端拿数据会涉及到跨域CORS的问题(前后端都需要设置)
1.后端
我们现在为前端和后端分别设置两个不同的域名
window 系统: C:\Windows\System32\drivers\etc\host
linux/mac系统: /etc/hosts
位置 | 域名 |
---|---|
前端 | www.luffycity.cn |
后端 |
编辑/etc/hosts
文件,可以设置本地域名
在文件中增加两条信息
127.0.0.1 api.luffycity.cn
127.0.0.1 www.luffycity.cn
通过浏览器访问drf项目,会出现以下错误信息
可以通过settings的ALLOWED_HOSTS,设置允许访问
# 设置哪些客户端可以通过地址访问到后端 ALLOWED_HOSTS = [ 'api.luffycity.cn', 'www.luffycity.cn', 'localhost', # 实际开发的时候不会写上localhost和127.0.0.1的 '127.0.0.1', ]
现在,前端与后端分处不同的域名,我们需要为后端添加跨域访问的支持。
否则前端无法使用axios无法请求后端提供的api数据
我们使用CORS来解决后端对跨域访问的支持。
使用django-cors-headers扩展
在 Response(headers={"Access-Control-Allow-Origin":'客户端地址/*'})
pip install django-cors-headers
添加应用
INSTALLED_APPS = ( ... 'corsheaders', ... )
中间层设置【必须写在第一个位置】
MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ... ]
添加白名单
# CORS组的配置信息 CORS_ORIGIN_WHITELIST = ( '127.0.0.1:8080', 'localhost:8080', 'www.luffycity.cn:8080' ) CORS_ALLOW_CREDENTIALS = True # 允许ajax跨域请求时携带cookie
完成了上面的步骤,我们就可以通过后端提供数据给前端使用ajax访问了。
2.前端的配置
安装
npm install axios -S
导入
在main.js导入
// 导入axios import axios from 'axios'; // 从node_modules目录中导入包 // 设置vue的全局子对象 Vue.prototype.$axios = axios; // 把对象挂载vue中

2. 显示登陆页面
Login.vue,代码:

<template> <div class="login box"> <img src="https://www.luffycity.com/static/img/Loginbg.3377d0c.jpg" alt=""> <div class="login"> <div class="login-title"> <img src="https://www.luffycity.com/static/img/Logotitle.1ba5466.png" alt=""> <p>帮助有志向的年轻人通过努力学习获得体面的工作和生活!</p> </div> <div class="login_box"> <div class="title"> <span @click="login_type=1" :class="login_type==1?'current':''">密码登录</span> <span @click="login_type=2" :class="login_type==2?'current':''">短信登录</span> </div> <div class="inp" :class="login_type==1?'show':''"> <input v-model = 'username' type="text" placeholder="用户名 / 手机号码" class="user"> <input v-model = 'password' type="password" name="" class="pwd" placeholder="密码"> <div id="geetest1" title="验证码"></div> <div class="rember"> <p> <input type="checkbox" class="no" name="a"></input> <span>记住密码</span> </p> <p>忘记密码</p> </div> <button class="login_btn">登录</button> <p class="go_login" >没有账号 <span>立即注册</span></p> </div> <div class="inp" :class="login_type==2?'show':''"> <input v-model = 'username' type="text" placeholder="手机号码" class="user"> <input v-model = 'password' type="password" name="" class="pwd" placeholder="短信验证码"> <div class="rember"> <p> <input type="checkbox" class="no" name="a"></input> <span>记住密码</span> </p> <p>忘记密码</p> </div> <button class="login_btn">登录</button> <p class="go_login" >没有账号 <span>立即注册</span></p> </div> </div> </div> </div> </template> <script> export default{ name:"Login", data(){ return { login_type:2, username:"", password:"", } }, components:{ } } </script> <style scoped> .box{ width: 100%; position: relative; } .box img{ width: 100%; } .box .login { position: absolute; width: 500px; height: 400px; top: 50%; left: 50%; margin-left: -250px; margin-top: -300px; } .login .login-title{ width: 100%; text-align: center; } .login-title img{ width: 190px; height: auto; } .login-title p{ font-family: PingFangSC-Regular; font-size: 18px; color: #fff; letter-spacing: .29px; padding-top: 10px; padding-bottom: 50px; } .login_box{ width: 400px; height: auto; background: #fff; box-shadow: 0 2px 4px 0 rgba(0,0,0,.5); border-radius: 4px; margin: 0 auto; padding-bottom: 40px; } .login_box .title{ font-size: 20px; color: #9b9b9b; letter-spacing: .32px; border-bottom: 1px solid #e6e6e6; display: flex; justify-content: space-around; padding: 50px 60px 0 60px; margin-bottom: 20px; cursor: pointer; } .login_box .title .current{ color: #4a4a4a; border-bottom: 2px solid #84cc39; } .inp{ width: 350px; margin: 0 auto; display: none; } .show{ display: block; } .inp input{ border: 0; outline: 0; width: 100%; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp input.user{ margin-bottom: 16px; } .inp .rember{ display: flex; justify-content: space-between; align-items: center; position: relative; margin-top: 10px; } .inp .rember p:first-of-type{ font-size: 12px; color: #4a4a4a; letter-spacing: .19px; margin-left: 22px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; /*position: relative;*/ } .inp .rember p:nth-of-type(2){ font-size: 14px; color: #9b9b9b; letter-spacing: .19px; cursor: pointer; } .inp .rember input{ outline: 0; width: 30px; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp .rember p span{ display: inline-block; font-size: 12px; width: 100px; /*position: absolute;*/ /*left: 20px;*/ } #geetest{ margin-top: 20px; } .login_btn{ width: 100%; height: 45px; background: #84cc39; border-radius: 5px; font-size: 16px; color: #fff; letter-spacing: .26px; margin-top: 30px; } .inp .go_login{ text-align: center; font-size: 14px; color: #9b9b9b; letter-spacing: .26px; padding-top: 20px; } .inp .go_login span{ color: #84cc39; cursor: pointer; } </style>
在routes/index.js中,添加路由
import Vue from "vue" import Router from "vue-router" // 导入需要注册路由的组件 import Home from "../components/Home" import Login from "../components/Login" Vue.use(Router); // 配置路由列表 export default new Router({ mode:"history", routes:[ // 路由列表 { name:"Home", path: "/home", component:Home, }, { name:"Home", path: "/", component:Home, }, { name:"Login", path: "/login", component:Login, } ] })
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器