路飞学城项目 后台主页模块设计,simpleui后台管理,轮播图接口,跨域问题详解,前台主页功能
目录
一、后台主页模块设计
# 1 创建后台主页模块(一个模块一个app)
python ../../manage.py startapp home
# 2 在models中写轮播图表
-写一个基表BaseModel
-写轮播图表
from django.db import models
class BaseModel(models.Model):
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
is_delete = models.BooleanField(default=False, verbose_name='是否删除')
is_show = models.BooleanField(default=True, verbose_name='是否上架')
orders = models.IntegerField(verbose_name='优先级')
class Meta:
abstract = True # 这个表模型只用来继承,不用来在数据库中生成表
迁移,别忘记注册和回到根路径
from utils.common_models import BaseModel
class Banner(BaseModel):
# 1 id img图片地址 长传时间 是否删除 是否显示 order
# 把公共字段抽取到某个基表中,以后要使用,直接继承基表,扩写自己的字段就可以了---》用过的AbstractUser就是这个原理
title = models.CharField(max_length=16, unique=True, verbose_name='名称')
image = models.ImageField(upload_to='banner', verbose_name='图片')
# 点击图片,调整到的路径
# 前端跳转的地址: 前端路由 完整的http链接
link = models.CharField(max_length=64, verbose_name='跳转链接')
info = models.TextField(verbose_name='详情') # 也可以用详情表,宽高出处
class Meta:
db_table = 'luffy_banner'
verbose_name_plural = '轮播图表'
def __str__(self):
return self.title
二、simpleui后台管理
1.下载:pip install django-simpleui
2.注册 'simpleui'
3.国际化
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
4.注册超级管理员
python manage.py createsuperuser
yzk
yzk12345
5.去admin.py里面注册
from .models import Banner
admin.site.register(Banner)
6.去apps里面加入,录入数据
class HomeConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'home'
verbose_name = '首页功能'
7.配置文件
MEDIA_URL = 'media/' # 以后所有的在meida文件夹下的资源自动拼接 media/
# 正常在公司中,网站分主站和后台管理
# 后台管理,主要是运营录入数据,使用simpleui
三、轮播图接口
3.1 封装自己的mixin
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
RetrieveModelMixin
from utils.common_response import APIResponse
class CommonListModelMixin(ListModelMixin):
def list(self, request, *args, **kwargs):
res = super().list(request, *args, **kwargs)
return APIResponse(data=res.data)
class CommonCreateModelMixin(CreateModelMixin):
def create(self, request, *args, **kwargs):
res = super().create(request, *args, **kwargs)
return APIResponse(data=res.data) # {code:100,msg:成功,data:{}}
class CommonUpdateModelMixin(UpdateModelMixin):
def update(self, request, *args, **kwargs):
res = super(CommonUpdateModelMixin, self).update(request, *args, **kwargs)
return APIResponse(data=res.data)
class CommonRetrieveModelMixin(RetrieveModelMixin):
def retrieve(self, request, *args, **kwargs):
res = super(CommonRetrieveModelMixin, self).retrieve(request, *args, **kwargs)
return APIResponse(data=res.data)
class CommonDestroyModelMixin(DestroyModelMixin):
def destroy(self, request, *args, **kwargs):
res = super().destroy(request, *args, **kwargs)
return APIResponse(msg='删除成功')
3.2 轮播图接口
视图
# 查询所有 轮播图接口
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .serialzier import BannerSerializer
from .models import Banner
from utils.common_mixin import CommonListModelMixin as ListModelMixin
class BannerView(GenericViewSet, ListModelMixin):
queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')
serializer_class = BannerSerializer
路由
## 总路由
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/home/', include('home.urls')),
# static 默认开启的,后期咱们会开启media文件夹,除此之外的其它文件夹,尽量不要开放,让外部访问
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
]
# 分路由
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
from .views import BannerView
router.register('banner', BannerView, 'banner')
urlpatterns = [
# path('admin/', admin.site.urls),
]
urlpatterns += router.urls
序列化类
from rest_framework import serializers
from .models import Banner
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = ['id', 'image', 'title', 'link']
3.3 自定义配置文件
# 通过配置文件控制显示多少条轮播图
# 使用步骤
1 在settings下新建:common_settings.py
BANNER_COUNT = 3
2 在dev.py中加入
from .common_settings import *
3 在轮播图接口上
class BannerView(GenericViewSet, ListModelMixin):
queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
serializer_class = BannerSerializer
四、跨域问题详解
# 以后只要前后端分离项目,都会出现跨域问题,咱们要解决
# 同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现
请求的url地址,必须与浏览器上的url地址处于同域上:也就是[域名],[端口],[协议]相同.
http://127.0.0.1:8080
ftf://127.0.0.1:8080
比如:我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001一段数据
浏览器上就会报错,个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险
请求发送,服务的执行,数据也正常返回,只是被浏览器拦截了
# 正因为同源策略的存在,咱们写前后端分离的项目,无法正常获取到数据
# 解决跨域问题:
1 jsonp 跨域(不了解)
2 跨域资源共享(CORS) 后端技术
3 Nginx代理
# CORS:跨域资源共享
CORS需要浏览器和服务器同时支持,所有浏览器都支持该功能
只需要服务的处理即可:只需要在在响应头中加入固定的头就实现cors---》比如在响应头中加入Access-Control-Allow-Origin='*'---->get请求就没有跨域了---》但是put请求还会有
# cors的请求分两种
-简单请求,浏览器直接发起
-非简单请求,浏览器先发送要给options预检请求,服务端允许,再发送真正的请求
# 什么是简单请求,什么是非简单请求
# 如果属于下面,就是简单请求
1 请求方法是以下三种方法之一:
HEAD
GET
POST
2 HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
# 使用cors解决跨域,就是再响应头中加入固定的一些东西,专门写个中间件
res['Access-Control-Allow-Headers'] = 'token'
res['Access-Control-Allow-Methods'] = 'DELETE'
res['Access-Control-Allow-Origin'] = 'http://192.168.1.252:8080'
### 补充:######
前端访问的后端地址,一定要准确
3.0 自定义中间件,解决跨域问题
##### common_mideleware.py
from django.utils.deprecation import MiddlewareMixin
### 自定义中间件解决跨域问题---》以后其它框架都是这个原理---》django上有人做了
class CorsMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if request.method == 'OPTIONS':
response['Access-Control-Allow-Headers'] = 'token'
response['Access-Control-Allow-Methods'] = 'DELETE'
response['Access-Control-Allow-Origin'] = '*'
return response
### 配置文件配置中间件
MIDDLEWARE = [
'utils.common_mideleware.CorsMiddleware'
]
3.1 django-cors-headers
####使用pip安装
pip install django-cors-headers
#####添加到setting的app中
INSTALLED_APPS = (
...
'corsheaders',
...
)
#### 添加中间件
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
...
]
#### 4、setting下面添加下面的配置
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'Pragma',
'token'
)
3.2 django-cors-headers源码
# 核心代码再中间件的---》process_response 3.0.14 版本
class CorsMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if (
not conf.CORS_ALLOW_ALL_ORIGINS
and not self.origin_found_in_white_lists(origin, url)
and not self.check_signal(request)
):
return response
if conf.CORS_ALLOW_ALL_ORIGINS and not conf.CORS_ALLOW_CREDENTIALS:
response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*"
else:
response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin
if request.method == "OPTIONS":
response[ACCESS_CONTROL_ALLOW_HEADERS] = ", ".join(conf.CORS_ALLOW_HEADERS)
response[ACCESS_CONTROL_ALLOW_METHODS] = ", ".join(conf.CORS_ALLOW_METHODS)
return response
4 前台主页功能
4.1 Banner.vue
<template>
<div>
<el-carousel height="400px">
<el-carousel-item v-for="item in banner_list" :key="item">
<!-- router-link只能跳内部,不能跳外部-->
<router-link :to="item.link" v-if="item.link.indexOf('http:')<0">
<img :src="item.image" :alt="item.title">
</router-link>
<a :href="item.link" v-else>
<img :src="item.image" :alt="item.title">
</a>
</el-carousel-item>
</el-carousel>
</div>
</template>
<script>
export default {
name: "Banner",
data() {
return {banner_list: []}
},
created() {
this.$axios.get(`${this.$settings.BASE_URL}home/banner/`).then(res => {
this.banner_list = res.data.data
})
}
}
</script>
<style scoped>
.el-carousel__item h3 {
color: #475669;
font-size: 18px;
opacity: 0.75;
line-height: 300px;
margin: 0;
}
.el-carousel__item {
height: 400px;
min-width: 1200px;
}
.el-carousel__item img {
height: 400px;
margin-left: calc(50% - 1920px / 2);
}
</style>
4.2 Footer.vue
<template>
<div class="footer">
<ul>
<li>关于我们</li>
<li>联系我们</li>
<li>商务合作</li>
<li>帮助中心</li>
<li>意见反馈</li>
<li>新手指南</li>
</ul>
<p>Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p>
</div>
</template>
<script>
export default {
name: "Footer"
}
</script>
<style scoped>
.footer {
width: 100%;
height: 128px;
background: #25292e;
color: #fff;
}
.footer ul {
margin: 0 auto 16px;
padding-top: 38px;
width: 810px;
}
.footer ul li {
float: left;
width: 112px;
margin: 0 10px;
text-align: center;
font-size: 14px;
}
.footer ul::after {
content: "";
display: block;
clear: both;
}
.footer p {
text-align: center;
font-size: 12px;
}
</style>
4.3 Header.vue
<template>
<div class="header">
<div class="slogan">
<p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p>
</div>
<div class="nav">
<ul class="left-part">
<li class="logo">
<router-link to="/">
<img src="../assets/img/head-logo.svg" alt="">
</router-link>
</li>
<li class="ele">
<span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span>
</li>
<li class="ele">
<span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span>
</li>
<li class="ele">
<span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span>
</li>
</ul>
<div class="right-part">
<div>
<span>登录</span>
<span class="line">|</span>
<span>注册</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Header",
data() {
return {
url_path: sessionStorage.url_path || '/',
}
},
methods: {
goPage(url_path) {
// 已经是当前路由就没有必要重新跳转
if (this.url_path !== url_path) {
this.$router.push(url_path);
}
sessionStorage.url_path = url_path;
},
},
created() {
sessionStorage.url_path = this.$route.path;
this.url_path = this.$route.path;
}
}
</script>
<style scoped>
.header {
background-color: white;
box-shadow: 0 0 5px 0 #aaa;
}
.header:after {
content: "";
display: block;
clear: both;
}
.slogan {
background-color: #eee;
height: 40px;
}
.slogan p {
width: 1200px;
margin: 0 auto;
color: #aaa;
font-size: 13px;
line-height: 40px;
}
.nav {
background-color: white;
user-select: none;
width: 1200px;
margin: 0 auto;
}
.nav ul {
padding: 15px 0;
float: left;
}
.nav ul:after {
clear: both;
content: '';
display: block;
}
.nav ul li {
float: left;
}
.logo {
margin-right: 20px;
}
.ele {
margin: 0 20px;
}
.ele span {
display: block;
font: 15px/36px '微软雅黑';
border-bottom: 2px solid transparent;
cursor: pointer;
}
.ele span:hover {
border-bottom-color: orange;
}
.ele span.active {
color: orange;
border-bottom-color: orange;
}
.right-part {
float: right;
}
.right-part .line {
margin: 0 10px;
}
.right-part span {
line-height: 68px;
cursor: pointer;
}
</style>
4.4 HomeView.vue
<template>
<div class="home">
<Header></Header>
<Banner></Banner>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<Footer></Footer>
</div>
</template>
<script>
import Banner from "@/components/Banner";
import Footer from '@/components/Footer'
import Header from "@/components/Header";
export default {
name: 'HomeView',
// created() {
//
// this.$axios.get('http://127.0.0.1:8000/api/v1/home/test/', {
// headers: {
// token: 'sdfafasdf'
// }
//
// }).then(res => {
// console.log(res.data.data)
// })
// }
components: {
Header, Footer, Banner
}
}
</script>