后台主页模块设计
步骤
1.创建后台主页模块(一个模块一个app)
python ../../manage.py startapp home ps:路径切换至app里面
2.在models中写轮播图表
-写一个基表BaseModel
表中有些字段,其它表也会有--->创建一个基表,以后其它表继承该表--->只用来做继承
-写轮播图表
3.迁移
补充:DateTimeField的属性
1.auto_now_add:这个字段新增的时候,可以不传,会以当前时间存入
这样写,配置文件中:USE_TZ = False写成true,和fasle的区别
2.auto_now:更新这条记录,会把当前时间存入
update更新
直接:表.objects.filter().update()
对象.属性=xx --->对象.save()
default=datetime.datetime.now 也会设置当前时间,不要加括号
Utils/commcon_models.py
from django.db import models
class BaseModel(models.Model):
create_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 # 这个表模型只用来继承,不用来在数据库中生成表
home/models.py
from django.db import models
# Create your models here.
from utils.common_models import BaseModel
class Banner(BaseModel):
# 1.需要的字段:id img图片地址 上传时间 是否删除 是否显示 order
# 2.上传时间 是否删除 是否显示 order-->很多地方用得到-->把公共字段抽取到某个基表中,以后要使用,直接继承基表,扩写自己的字段就可以了--->用过的AbstractUser就是这个原理
title = models.CharField(max_length=16,unique=True,verbose_name='名称')
image = models.ImageField(upload_to='banner',verbose_name='图片')
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
迁移
python manage.py makemigrations
python manage.py migrate
Simpleui后台管理
1.安装:pip install django-simpleui
2.在配置文件中注册:simpleui
INSTALLED_APPS = [
'simpleui',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
...
]
3.创建超级用户:python manage.py createsuperuser
4.在admin注册表:
from django.contrib import admin
from .models import Banner
admin.register(Banner)
5.改后台语言,中文、时间
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
6.登陆,录入数据
ps:补充:
正常在公司中,网站分主站和后台管理
后台管理,主要是运营录入数据,使用simpleui
轮播图接口
路由分发
# 总路由
from django.urls import path,include
urlpatterns = [
path('api/v1/home/',include('home.urls')),
]
编写视图函数、序列化类、自定义配置文件--初步
from rest_framework import serializers
from .models import Banner
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = ['id','title','image','link']
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .models import Banner
from django.conf import settings
from .serializer import BannerSerializer
class BannerView(GenericViewSet,ListModelMixin):
queryset = Banner.objects.filter(is_delete=False,is_show=True)[:settings.BANNER_COUNT]
serializer_class = BannerSerializer
ps:想定义自己的格式,所以自己写一个mixin
1.在settings下新建,common_settings.py
BANNER_COUNT = 4
2.在settings的dev.py中导入:from .common_settings import *
封装自己的mixin
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin,UpdateModelMixin
from utils.common_response import APIResponse
class CommonListModelMixin(ListModelMixin):
def list(self, request, *args, **kwargs):
res = super(CommonListModelMixin,self).list(request, *args, **kwargs)
return APIResponse(data=res.data)
class CommonRetrieveModelMixinn(RetrieveModelMixin):
def retrieve(self, request, *args, **kwargs):
res = super().retrieve(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)
class CommonDestroyModelMixin(DestroyModelMixin):
def destroy(self, request, *args, **kwargs):
res = super().destroy(request, *args, **kwargs)
return APIResponse(msg='删除成功')
class CommonUpdateModelMixin(UpdateModelMixin):
def update(self, request, *args, **kwargs):
res = super().update(request, *args, **kwargs)
return APIResponse(data=res.data)
视图函数更新
from rest_framework.viewsets import GenericViewSet
from .models import Banner
from django.conf import settings
from .serializer import BannerSerializer
from utils.common_mixin import CommonListModelMixin as ListModelMixin
class BannerView(GenericViewSet,ListModelMixin):
queryset = Banner.objects.filter(is_delete=False,is_show=True)[:settings.BANNER_COUNT]
serializer_class = BannerSerializer
跨域问题
同源策略
1.同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现
2.请求的url地址,必须与浏览器上的url地址处于同域上,也就是【域名】,【端口】,【协议】相同.
eg:我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001一段数据
浏览器上就会报错,这就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险
3.请求发送,服务的执行,数据也正常返回,只是被浏览器拦截了,正因为同源策略的存在,咱们写前后端分离的项目,无法正常获取到数据
ps:浏览器的同源策略:浏览器不允许向不同域发请求[地址,端口,协议],请求发送成功了,服务端也响应了,但是被浏览器拦截了
使用cors:跨域资源共享,解决这个问题----》再响应头中加入对应的响应头
解决跨域问题
1. jsonp 跨域(不了解)-->比如标签上的图片地址...
2. 跨域资源共享(CORS-->后端技术
3.Nginx代理
CORS:跨域资源共享
简介
1.CORS需要浏览器和服务器同时支持,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
2.只需要服务的处理即可:只需要在在响应头中加入固定的头就实现cors--->比如在响应头中加入Access-Control-Allow-Origin='*'---->get请求就没有跨域了---->但是put请求还会有
cors的请求分两种
1.cors的请求分两种:
1.简单请求,浏览器直接发起
2.非简单请求,浏览器先发送要给options预检请求,服务端允许,再发送真正的请求
2.什么是简单请求,什么是非简单请求
如果属于下面,就是简单请求:
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)
测试
def test(request):
res = HttpResponse('ok')
res['Access-Control-Allow-Origin']='http://localhost:8080'
res['Access-Control-Allow-Headers']='token'
return res
def test(request):
res = HttpResponse('ok')
if request.method == 'OPTIONS':
print(11)
res['Access-Control-Allow-Methods']='DELETE'
res['Access-Control-Allow-Headers']='token'
res['Access-Control-Allow-Origin'] = 'http://localhost:8080'
return res
自定义中间件,解决跨域问题
1.自定义中间件解决跨域问题--->以后其它框架都是这个原理--->django上有人做了-->django-cors-headers
2.自定义中间件:
from django.middleware.security import SecurityMiddleware
from django.utils.deprecation import MiddlewareMixin
class CommonMiddle(MiddlewareMixin):
def process_response(self, request, response):
if request.method == 'OPTIONS':
response['Access-Control-Allow-Headers'] = 'token'
response['Access-Control-Allow-Methods'] = 'DELETE,PUT'
response['Access-Control-Allow-Origin'] = '*'
return response
3.配置文件配置中间件:
MIDDLEWARE = [
...,
'utils.common_middleware.CommonMiddle'
]
django-cors-headers
1.安装:pip install django-cors-headers
2.添加到setting的app中:
INSTALLED_APPS = (
...
'corsheaders',
...
)
3.添加中间件
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'
)
django-cors-headers源码
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
前台主页功能
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>
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>
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>
HomeView.vue
<template>
<div class="home">
<Header></Header>
<Banner></Banner>
<Footer></Footer>
</div>
</template>
<script>
import Header from "@/components/Header.vue";
import Footer from "@/components/Footer.vue";
import Banner from "@/components/Banner.vue";
export default {
name: 'HomeView',
components: {
Header, Footer, Banner
}
}
</script>
补充
1.表模型继承ModelMixin,所有表模型都有to_dict,序列化用的
2.以后拿到表模型对象:序列化后的数据=banner.to_dict(a=['id','name'])
参照开源项目:https://gitee.com/openspug/spug