Python笔记_70_创建后端项目_搭建前端项目_跨域CORS

创建后端项目

mkdir luffy        # 这里是项目根目录,后面存放前后端2个项目在里面
cd luffy
django-admin startproject luffyapi
调整目录
├── docs/          # 项目相关资料保存目录
├── logs/          # 项目运行时/开发时日志目录
├── manage.py
├── luffyapi/         # 项目主应用,开发时的代码保存
│   ├── apps/      # 开发者的代码保存目录,以模块[子应用]为目录保存
│   ├── libs/      # 第三方类库的保存目录[第三方组件、模块]
│   ├── settings/
│       ├── dev.py   # 项目开发时的本地配置
│       ├── prod.py  # 项目上线时的运行配置
│   ├── urls.py    # 总路由
│   ├── utils/     # 多个模块[子应用]的公共函数类库[自己开发的组件]
└── scripts/       # 保存项目运营时的脚本文件

在这里插入图片描述

  • 分不同环境进行项目配置
    开发者本地的环境、目录、数据库密码和线上的服务器都会不一样,所以我们的配置文件可以针对不同的系统分成多分.
  1. 在项目主应用下,创建一个settings的配置文件存储目录
  2. 根据线上线下两种情况分别创建2个配置文件 dev.pyprod.py
  3. 把原来项目主应用的 settings.py配置内容复制2份到dev.pyprod.py里面
  4. 把原来的settings.py配置文件修改文件名或者删除

根据在manage.py中根据不同的情况导入对应的配置文件了

在这里插入图片描述

日志配置

settings/dev.py文件中追加如下配置:

# 日志配置
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录必须手动创建
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            'backupCount': 10,
            # 日志格式:详细格式
            'formatter': 'verbose'
        },
    },
    # 日志对象
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
        },
    }
}
异常处理

新建utils/exceptions.py

from rest_framework.views import exception_handler

from django.db import DatabaseError
from rest_framework.response import Response
from rest_framework import status

import logging
logger = logging.getLogger('django')


def custom_exception_handler(exc, context):
    """
    自定义异常处理
    :param exc: 异常类
    :param context: 抛出异常的上下文
    :return: Response响应对象
    """
    # 调用drf框架原生的异常处理方法
    response = exception_handler(exc, context)

    if response is None:
        view = context['view']
        if isinstance(exc, DatabaseError):
            # 数据库异常
            logger.error('[%s] %s' % (view, exc))
            response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

settings/dev.py配置文件中添加

REST_FRAMEWORK = {
    # 异常处理
    'EXCEPTION_HANDLER': 'luffyapi.utils.exceptions.custom_exception_handler',
}
创建数据库
create database luffy default charset=utf8mb4;

为当前项目创建数据库用户[这个用户只能看到这个数据库]

create user luffy_user identified by 'luffy';
grant all privileges on luffy.* to 'luffy_user'@'%';
flush privileges;

在这里插入图片描述

配置数据库连接

打开settings/dev.py文件,并配置

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "luffy_user",
        "PASSWORD": "luffy",
        "NAME": "luffy",
    }
}

在项目主模块的 __init__.py中导入pymysql

import pymysql

pymysql.install_as_MySQLdb()
调整错误

数据库版本检测导致的错误

[外链图片转存失败(img-jjXOPNXK-1565175799073)(assets/1557453985484.png)]

数据库的版本检测代码注释掉。

]

第二个错误也是因为数据库版本的默认编码导致,query返回的内容格式使用有误。

新增一行代码,把query查询结果转换格式为 bytes类型

[(img-rziY9kLT-1565175799074)(assets/1557454044879.png)]

搭建前端项目

创建项目目录
cd ~/Desktop/luffy
vue init webpack lufei_pc

例如,我要把项目保存在~/Desktop桌面目录下,可以如下操作:

cd ~/Desktop
vue init webpack lufei_pc

根据需要在生成项目时,我们选择对应的选项, 效果:

[(img-kyzb4pwN-1565175799074)(assets/1557451009689.png)]

根据上面的提示,我们已经把vue项目构建好了,运行测试服务器。

[(img-l7bY2B23-1565175799075)(assets/1557451125218.png)]

打开项目已经,在pycharm的终端下运行vue项目,查看效果。

npm run dev

接下来,我们根据终端上效果显示的对应地址来访问项目(如果有多个vue项目在运行,8080端口被占据了,服务器会自动改端口,所以根据自己实际在操作中看到的地址来访问。)

访问:http://localost:8080。效果:

[(img-UQDjqicf-1565175799077)(assets/1557451188340.png)]

我们也可以把我们的前端项目进行git源代码管理

初始化前端项目

清除默认的HelloWorld组件和APP.vue中的默认样式和logo.png图片

[(img-fQRWQKXn-1565175799077)(assets/1557451289161.png)]

接下来,我们可以查看效果了,一张白纸~

[(img-aVBF4Rdq-1565175799077)(assets/1557451300406.png)]

安装路由vue-router
下载路由组件
npm i vue-router -S

执行效果:

[外链图片转存失败(img-d222NZC0-1565175799078)(assets/1557451350882.png)]

url : 协议://域名:端口/path?query_string#/home

配置路由
  • 初始化路由对象

src目录下创建routers路由目录,在routers目录下创建index.js路由文件

index.js路由文件中,编写初始化路由对象的代码 .

import Vue from "vue"
import Router from "vue-router"

// 这里导入可以让让用户访问的组件

Vue.use(Router);

export default new Router({
  // 设置路由模式为‘history’,去掉默认的#
  mode: "history",
  routes:[
    // 路由列表

  ]
})

[外链图片转存失败(img-jSXWGE73-1565175799079)(assets/1557451480681.png)]

  • 注册路由信息

打开main.js文件,把router对象注册到vue中。
代码:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './routers/index';

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
});

[外链图片转存失败(img-6BDZfCQM-1565175799080)(assets/1557451533378.png)]

  • 在视图中显示路由对应的内容

App.vue组件中,添加显示路由对应的内容。

[外链图片转存失败(img-yV1YWKcH-1565175799081)(assets/1557451572377.png)]

代码:

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
  components: {

  }
}
</script>

<style>

</style>

  • 创建并提供前端首页的组件
    routers/index.js
// import Vue from "vue"
// import Router from "vue-router"
//
//
// // 这里导入可以让让用户访问的组件
import Home from "../components/Home"
// Vue.use(Router);
//
// export default new Router({
//   // 设置路由模式为‘history’,去掉默认的#
//   mode: "history",
//   routes:[
//     // 路由列表
     {
       name:"Home",
       path:"/",
       component:Home,
     },
      {
       name:"Home",
       path:"/home",
       component:Home,
     },
   ]
// })

  • 创建Home组件
    components/Home.vue
<template>
  <div id="home">
    前端首页
  </div>
</template>
<script>
  export default {
      name:"Home",
      data(){
          return {
              
          }
      }
  }
</script>

<style scoped>

</style>

[外链图片转存失败(img-A4PZa4P6-1565175799081)(assets/1557451775965.png)]

前端初始化全局变量和全局方法

src目录下创建settings.js站点开发配置文件:

export default {
  Host:"http://127.0.0.1",
}

main.js中引入

// // The Vue build version to load with the `import` command
// // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
// import Vue from 'vue'
// import App from './App'
// import router from './routers/index';
import settings from "./settings"
// Vue.config.productionTip = false;
Vue.prototype.$settings = settings;
//
// /* eslint-disable no-new */
// new Vue({
//   el: '#app',
//   router,
//   components: { App },
//   template: '<App/>'
// });

引入ElementUI
npm i element-ui -S

上面的命令等同于

npm install element-ui --save

执行命令效果:

[外链图片转存失败(img-7DIBHXjq-1565175799082)(assets/1557452146253.png)]

配置ElementUI到项目中

main.js中导入ElementUI,并调用。

代码:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
// import Vue from 'vue'
// import App from './App'
// import router from './routers/index';

// 开发配置文件
// import settings from "./settings"
// Vue.prototype.$settings = settings;

// elementUI 导入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 调用插件
Vue.use(ElementUI);


// Vue.config.productionTip = false;

/* eslint-disable no-new */
// new Vue({
//   el: '#app',
//   router,
//   components: { App },
//   template: '<App/>'
// });

效果:

[外链图片转存失败(img-EZZBvfLh-1565175799082)(assets/1557452192638.png)]

成功引入了ElementUI以后,接下来我们就可以开始进入前端页面开发,首先是首页。

接下来我们把之前完成的首页,直接拿过来使用[注意除了组件以外,还有静态文件也需要拿过来,包括App.vue里面的公共样式],并运行项目。

App.vue,全局css初始化代码

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
/* 声明全局样式和项目的初始化样式 */
body,h1,h2,h3,h4,p,table,tr,td,ul,li,a,form,input,select,option,textarea{
  margin:0;
  padding: 0;
  font-size: 15px;
}
a{
  text-decoration: none;
  color: #333;
}
ul,li{
  list-style: none;
}
table{
  border-collapse: collapse; /* 合并边框 */
}

/* 工具的全局样式 */
.full-left{
  float: left!important;
}
.full-right{
  float: right!important;
}

[class*=" el-icon-"], [class^=el-icon-]{
  font-size: 50px;
}
.el-carousel__arrow{
  width: 120px;
  height: 120px;
}
.el-checkbox__input.is-checked .el-checkbox__inner,
.el-checkbox__input.is-indeterminate .el-checkbox__inner{
  background: #ffc210;
  border-color: #ffc210;
  border: none;
}
.el-checkbox__inner:hover{
  border-color: #9b9b9b;
}
.el-checkbox__inner{
  width: 16px;
  height: 16px;
  border: 1px solid #9b9b9b;
  border-radius: 0;
}
.el-checkbox__inner::after{
  height: 9px;
  width: 5px;
}

</style>

首页显示

添加代码

  • Home.vue
<template>
    <div class="home">
      <Header></Header>
      <Banner></Banner>
      <Footer></Footer>
    </div>
</template>

<script>
  import Header from "./common/Header"
  import Banner from "./common/Banner"
  import Footer from "./common/Footer"
  export default {
      name: "Home",
      data(){
        return {}
      },
      methods:{

      },
      components:{
        Header,
        Footer,
        Banner,
      }
  }
</script>

<style scoped>

</style>

  • components/common/Header.vue
<template>
    <div class="header-box">
      <div class="header">
        <div class="content">
          <div class="logo full-left">
            <router-link to="/"><img src="/static/image/logo.svg" alt=""></router-link>
          </div>
          <ul class="nav full-left">
              <li><span>免费课</span></li>
              <li><span>轻课</span></li>
              <li><span>学位课</span></li>
              <li><span>题库</span></li>
              <li><span>老男孩教育</span></li>
          </ul>
          <div class="login-bar full-right">
            <div class="shop-cart full-left">
              <img src="/static/image/cart.svg" alt="">
              <span><router-link to="/cart">购物车</router-link></span>
            </div>
            <div class="login-box full-left">
              <span>登录</span>
              &nbsp;|&nbsp;
              <span>注册</span>
            </div>
          </div>
        </div>
      </div>
    </div>
</template>

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

        }
      },
      created(){

      },
      methods:{

      }
    }
</script>

<style scoped>
.header-box{
  height: 80px;
}
.header{
  width: 100%;
  height: 80px;
  box-shadow: 0 0.5px 0.5px 0 #c9c9c9;
  position: fixed;
  top:0;
  left: 0;
  right:0;
  margin: auto;
  z-index: 99;
  background: #fff;
}
.header .content{
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
}
.header .content .logo{
  height: 80px;
  line-height: 80px;
  margin-right: 50px;
  cursor: pointer; /* 设置光标的形状为爪子 */
}
.header .content .logo img{
  vertical-align: middle;
}
.header .nav li{
  float: left;
  height: 80px;
  line-height: 80px;
  margin-right: 30px;
  font-size: 16px;
  color: #4a4a4a;
  cursor: pointer;
}
.header .nav li span{
  padding-bottom: 16px;
  padding-left: 5px;
  padding-right: 5px;
}
.header .nav li span a{
  display: inline-block;
}
.header .nav li .this{
  color: #4a4a4a;
  border-bottom: 4px solid #ffc210;
}
.header .nav li:hover span{
  color: #000;
}
.header .login-bar{
  height: 80px;
}
.header .login-bar .shop-cart{
  margin-right: 20px;
  border-radius: 17px;
  background: #f7f7f7;
  cursor: pointer;
  font-size: 14px;
  height: 28px;
  width: 88px;
  margin-top: 30px;
  line-height: 32px;
  text-align: center;
}
.header .login-bar .shop-cart:hover{
  background: #f0f0f0;
}
.header .login-bar .shop-cart img{
  width: 15px;
  margin-right: 4px;
  margin-left: 6px;
}
.header .login-bar .shop-cart span{
  margin-right: 6px;
}
.header .login-bar .login-box{
  margin-top: 33px;
}
.header .login-bar .login-box span{
  color: #4a4a4a;
  cursor: pointer;
}
.header .login-bar .login-box span:hover{
  color: #000000;
}
</style>
  • components/common/Bannner.vue
<template>
  <el-carousel height="720px" :interval="3000" arrow="always">
    <el-carousel-item>
      <img src="/static/image/alex.jpeg" alt="">
    </el-carousel-item>
    <el-carousel-item>
      <img src="/static/image/banner1.png" alt="">
    </el-carousel-item>
    <el-carousel-item>
      <img src="/static/image/banner1.png" alt="">
    </el-carousel-item>
  </el-carousel>
</template>

<script>
    export default {
        name: "Banner",
    }
</script>

<style scoped>
  .el-carousel__item h3 {
    color: #475669;
    font-size: 18px;
    opacity: 0.75;
    line-height: 300px;
    margin: 0;
  }

  .el-carousel__item:nth-child(2n) {
    background-color: #99a9bf;
  }

  .el-carousel__item:nth-child(2n+1) {
    background-color: #d3dce6;
  }
</style>


  • components/common/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>

也可以把App.vue的style标签的css代码放到static外部目录下引用过来

main.js

import "../static/css/reset.css";

reset.css

body,h1,h2,h3,h4,h5,ul,p{
    padding: 0;
    margin:0;
    font-weight: normal;
  }
  body{
    margin-top: 80px;
  }
  a{
    text-decoration: none;
    color: #4a4a4a;
  }
  a:hover{
    color: #000;
  }

  ul{
    list-style: none;
  }

  img{
    width: 100%;
  }

  .header .el-menu li .el-submenu__title{
    height: 26px!important;
    line-height: 26px!important;
  }
  .el-menu--popup{
    min-width: 140px;
  }
  .el-checkbox__inner{
    width:16px;
    height: 16px;
    border: 1px solid #999;
  }
  .el-checkbox__inner:after{
    width: 6px;
    height: 8px;
  }
  .el-form-item__content{
    margin-left:0px!important;
    width: 120px;
  }

跨域CORS

我们现在为前端和后端分别设置两个不同的域名:

位置域名
前端www.luffycity.cn
后端api.luffycity.cn

编辑/etc/hosts文件,可以设置本地域名

sudo vim /etc/hosts

在文件中增加两条信息

127.0.0.1   localhost
127.0.0.1   api.luffycity.cn
127.0.0.1   www.luffycity.cn

[外链图片转存失败(img-e2z5Wz1y-1565175799083)(assets/1557454299594.png)]

如果安装了NGINX服务

通过浏览器访问前端vue项目,会出现nginx的欢迎页面,主要因为我们当前项目已经有一个nginx监听了80端口,所以访问www.luffycity.cn网址时,会自动被转发到127.0.0.1本机,因为没有网址默认端口是80端口,所以被nginx进行处理了当前请求,因此我们暂时先把nginx关闭先。

# 查找nginx的进程
ps -ef|grep nginx
# 关闭进程
sudo kill -9 nginx进程号

[外链图片转存失败(img-KiC19mE0-1565175799084)(assets/1557454528245.png)]

关闭了nginx以后,访问www.luffy.cirty.cn网址,效果:

[外链图片转存失败(img-X10HYfN5-1565175799084)(assets/1557454604654.png)]

上面并不是错误,而是没人监听了这个地址和端口了,解决方法:

暂停运行前端项目,并修改配置文件config/index.js

    host: 'www.luffycity.cn', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: true,

保存修改信息,并重启项目

[外链图片转存失败(img-vOYd2OFx-1565175799085)(assets\1561511963800.png)]

通过浏览器访问drf项目,会出现以下错误信息

[外链图片转存失败(img-jc2A78BI-1565175799085)(assets/1557454965635.png)]

可以通过settings/dev.pyALLOWED_HOSTS,设置允许访问

# 设置哪些客户端可以通过地址访问到后端
ALLOWED_HOSTS = [
    'api.luffycity.cn',
]

[外链图片转存失败(img-XOzFIckq-1565175799085)(assets/1557455086164.png)]

django-cors-headers扩展跨域访问

让用户访问的时候,使用api.luffycity.cn:8000
修改pycharm的manage.py的配置参数

[外链图片转存失败(img-wqIReaRk-1565175799085)(assets/1557455152863.png)]

同时,之前的前端项目中,我们创建的src/settings.js文件中的后端接口地址也要对应修改:

export default {
  Host:"http://api.luffycity.cn:8000/"
}

现在,前端与后端分处不同的域名,我们需要为后端添加跨域访问的支持

否则前端无法使用axios无法请求后端提供的api数据,我们使用CORS来解决后端对跨域访问的支持。

使用django-cors-headers扩展

在 Response(headers={"Access-Control-Allow-Origin":'客户端地址/*'})

文档:https://github.com/ottoyiu/django-cors-headers/

安装

pip install django-cors-headers

添加子应用

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

中间层设置【必须写在第一个位置】

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]

setttings/dev.py,添加白名单

# CORS组的配置信息
CORS_ORIGIN_WHITELIST = (
    'www.luffycity.cn:8080',
)
CORS_ALLOW_CREDENTIALS = True  # 允许ajax跨域请求时携带cookie

[外链图片转存失败(img-m3NfSEAL-1565175799086)(assets/1557455675163.png)]

完成了上面的步骤,我们就可以通过后端提供数据给前端使用ajax访问了。

前端使用 axios就可以访问到后端提供给的数据接口,但是如果要附带cookie信息,前端还要设置一下。

前端引入axios插件并配置允许axios发送cookie信息[axios本身也不允许ajax发送cookie到后端]

npm i axios -S

[外链图片转存失败(img-Zaq6uiSt-1565175799086)(assets/1557455747921.png)]

main.js中引用axios插件

import axios from 'axios'; // 从node_modules目录中导入包
// 允许ajax发送请求时附带cookie
axios.defaults.withCredentials = true;

Vue.prototype.$axios = axios; // 把对象挂载vue中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值