luffy之前端页面,后端接口,CORS跨域请求

复习

# django 项目目录调整
	-想把app都统一放到一个文件夹下
    	-apps文件夹:切换到该文件夹执行 python ../../manage.py startapp app名字
        -配置文件有多个:开发环境,上线环境
        	-settings文件夹,创建 dev.py(原来的settings)   pro.py
            -django项目的启动,基于配置文件(配置文件中不能乱导入,可能会报错)
            -manage.py 中修改
            os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy.settings.dev')
        	-wsgi.py  中修改
            os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy.settings.pro')
        -libs文件夹:第三方模块,自己写的模块
        -utils文件夹:工具
        	-日志,全局异常,响应对象
        -项目根路径 scripts 文件夹,测试脚本
        -项目根路径 logs 文件夹,日志(日志要记录到文件中)
        -在配置文件中,想直接注册app名字就可以使用了(前提需要配置到环境变量里)
        	-配置到环境变量之后就可以,所以可以直接写
            -把apps路径加入到环境变量,小luffy路径也加入了
            -以后在py文件中,导入模块,选择有很多---》一般以最短路径导入
            -pycharm中,导入爆红,其实不错,把文件夹组成source root
            
# 配置日志
	-配置文件(字典),放到dev.py中
    -utils/logging---》
    	import logging
    	logger=getLogger('django')
    -以后要用,直接导入logger对象,对象.info或者对象.error
# 处理全局异常
	-drf的全局异常:统一返回格式
    -写一个函数common_exception_handler(exc, context)
    -exc:异常对象,context内会有request对象,view对象
    -执行一下原来的
    -一定要加日志:
     logger.error('系统错误:请求地址是:%s,请求的试图类是:%s,错误原因是:%s' % (path, view_name,str(exc)))
    -配置文件中配置
    
    
# 封装Response
	-以后返回对象时,咱们使用自己封装的(简化代码)
    -return APIResponse(token='')



# 前端配置
	-vue create luffy_front
    -app.vue ---》<router-view/>
   	-全局css,清除原有标签的格式---》main.js中引入
    -写了一个setting.js--->后端地址---》main.js
        import settings from "./assets/js/settings";
        Vue.prototype.$settings=settings
        
    -axios--->安装---》放到原型中
        import axios from "axios";
        Vue.prototype.$axios=axios
    -使用
    	this.$axios.get(this.$settings.base_url+'/home/')



# 魔法方法
	- __init__和__new__的区别
    	init:实例化类时候调用
        new:创建对象时调用  在init之前执行

今日内容

1 前台使用elementui,bootstrap,jQuery

# 使用饿了么UI
	-cnpm i element-ui -S
    -main.js中写入
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);
    
    
# bootstrap
    
    -cnpm install bootstrap@3
    -main.js中
    import 'bootstrap'
	import 'bootstrap/dist/css/bootstrap.min.css'
    
 # jq
	-cnpm install jquery
    -在项目根路径下新建:vue.config.js
    const webpack = require("webpack");
    module.exports = {
        configureWebpack: {
            plugins: [
                new webpack.ProvidePlugin({
                    $: "jquery",
                    jQuery: "jquery",
                    "window.jQuery": "jquery",
                    "window.$": "jquery",
                    Popper: ["popper.js", "default"]
                })
            ]
        }
    };
    -一定要重启

2 后台User模块,User表

# 补充:软件开发模式
	-bbs项目:瀑布开发模式
    	瀑布开发模式就好比是火箭发射,工作和任务是预先计划好的,启动之后调整和返回难度很大。
    -路飞项目:敏捷开发
		敏捷开发模式就像是汽车驾驶,有很多条路可以到达目的地,可以随时根据导航反馈的路况进行及时调整。这个调整过程称为“开发—测量—认知”的反馈循环。

# 用户表:使用auth 的user表,自定义user表
# 咱们用了auth的user表----》扩写一些字段
# 创建一个app   user 
	python ../../manage.py startapp user
# user  app的models中写表
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    mobile = models.CharField(max_length=11, unique=True)  # 唯一
    # 需要pillow包的支持
    icon = models.ImageField(upload_to='icon', default='icon/default.png')

    class Meta:
        db_table = 'luffy_user'  # 如果不配它,表名是  app名字_类名小写
        verbose_name = '用户表'  # 在admin中显示的表名
        verbose_name_plural = verbose_name  # 在admin中显示的表名,不配它会加个s

    def __str__(self):  # print(对象) 的时候,触发它的执行
        return self.username
    
 # 在配置文件中配置
	AUTH_USER_MODEL = "app名.表名"
	AUTH_USER_MODEL='user.user'
 # 迁移数据库
	python manage.py makemigrations
    python manage.py migrate
    
    
    
#### 注意:
	-如果是空项目,之前数据库没有表,这么做完全没问题
    -但如果你之前迁移过数据库,这样就不行了
    	-因为user表已经存在了
        -第一步:删库
        -第二部:删除迁移文件(所有app的迁移文件)
        -第三步:删除django内置app的迁移文件(auth和admin这个app下migrations)
        -第四步:从新迁移数据库
        python manage.py makemigrations
    	python manage.py migrate

        
        
# 进行media的配置 图片的配置
	##1 dev.py中
        MEDIA_URL = '/media/'  #luffy下的/media文件夹
        MEDIA_ROOT = os.path.join(BASE_DIR, 'media') #拼接上
    ## 2 开路由
    from django.views.static import serve
    from django.conf import settings    #用django的settings他会反射到自己写的MEDIA_ROOT
    urlpatterns = [
        #有名分组 
        path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT})
    ]

3 前台首页设计

组件化开发  写三个组件 分别是导航栏 轮播图 尾部 写在components
						Header Footer Banner

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>

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>

Banner.vue

<template>
    <el-carousel :interval="4000" type="card" height="400px">
        <el-carousel-item v-for="item in 4" :key="item">
            <!--            <h3 class="medium">{{ item }}</h3>-->
            <img src="../assets/image/banner1.png" alt="">
        </el-carousel-item>
    </el-carousel>
</template>

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

        },
        created() {

        }
    }
</script>

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

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

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

    .el-carousel__item {
        height: 400px;
        /*min-width: 1200px;*/
    }

    .el-carousel__item img {
        height: 400px;
        margin-left: calc(50% - 1920px / 2);
    }
</style>

Home.vue

<template>
    <div class="home">
        <Header></Header>
        <Banner></Banner>
        <div class="course">
            <el-row>
                <el-col :span="6" v-for="(o, index) in 8" :key="o">
                    <el-card :body-style="{ padding: '0px' }" class="card">
                        <img src="https://img0.baidu.com/it/u=1396426037,2146632168&fm=253&fmt=auto&app=138&f=JPEG?w=631&h=500"
                             class="image">
                        <div style="padding: 14px;">
                            <span>崽种,瞅我</span>
                            <div class="bottom clearfix">
                                <time class="time">{{ currentDate }}</time>
                                <el-button type="text" class="button">别TM点</el-button>
                            </div>
                        </div>
                    </el-card>
                </el-col>
            </el-row>

        </div>
        <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 Header from "../components/Header";
    import Footer from "../components/Footer";
    import Banner from "../components/Banner";

    export default {
        name: 'Home',
        created() {
            // this.$axios.get(this.$settings.base_url + '/home/test2/').then(res => {
            //     console.log(res.data)
            // })

        },
        data() {
            return{
                currentDate: new Date()
            };

        },
        methods: {

        },
        components: {
            Header, Footer, Banner
        }

    }
</script>

<style>
    .time {
        font-size: 13px;
        color: #999;
    }

    .bottom {
        margin-top: 13px;
        line-height: 12px;
    }

    .button {
        padding: 0;
        float: right;
    }

    .image {
        width: 100%;
        display: block;
    }

    .clearfix:before,
    .clearfix:after {
        display: table;
        content: "";
    }

    .clearfix:after {
        clear: both
    }

    .course {
        margin-left: 20px;
        margin-right: 20px;
    }

    .card {
        margin: 30px;
    }
</style>

4 后台首页相关接口(轮播图图片的接口)

# 创建公共表 utils/nodels
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  #写了这个虚拟表不生成
#创建home下的banner表,并添加自己的字段
from django.db import models

# Create your models here.


from utils.models import BaseModel


class Banner(BaseModel):
    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
    
# 写一个方法
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .models import Banner
from .Serializer import BannerSerializer
class BannerView(GenericViewSet,ListModelMixin):
    serializer_class =BannerSerializer
    queryset = Banner.objects.all().filter(is_delete=False,is_show=True)

#序列化类也要写
from .models import Banner
from rest_framework import serializers


class BannerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Banner
        fields = ['title','image','link']

 #自动生成路由
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('banner', views.BannerView, 'banner')


urlpatterns += router.urls


5 跨域请求详解(重要)

# 如何保证自己的接口不被第三方调用


# 浏览器的安全策略:同源策略
	-请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.
    -如果不一样浏览器上就会报错,这个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险
    -请求,服务的执行了,数据返回了,但是浏览器拦截掉了
    
    
# 解决同源策略导致的数据不能正常加载

# csrf 跨站请求伪造
# xss  跨站脚本攻击
# CORS:跨域资源共享---》后端技术(后端代码中加东西)---》通过它,就可以解决跨域问题
	-CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能
    
    -浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
    	-如果请求满足以下两种情况,就是简单请求,否则就是非简单请求
        (1) 请求方法是以下三种方法之一:
            HEAD
            GET
            POST
        (2)HTTP的头信息不超出以下几种字段:
            Accept
            Accept-Language
            Content-Language
            Last-Event-ID
            Content-Type:只限于三个值urlencoded、form-data、text/plain
            
      	-简单请求发送一次请求,数据拿回来,但是被浏览器同源策略拦截了
        -非简单请求发送两次,一次options请求,如果允许再发真正的请求,如果不允许就不发了
        
     -通过跨域资源共享解决跨域
    	-简单请求解决:其实就是在响应头中加一些东西
            res=HttpResponse('ok')
            #请求头加参数
            res['Access-Control-Allow-Origin']='*'
            return res
        -非简单请求的解决,也是在响应头中加东西,中间件记得注释csrf   如果用drf不用
        	if request.method == 'OPTIONS':
                # res['Access-Control-Allow-Methods'] = 'DELETE,'
                res['Access-Control-Allow-Headers'] = 'Content-Type'
                
      -最好:写一个中间件,
        from django.utils.deprecation import MiddlewareMixin
        class CORSMiddle(MiddlewareMixin):
            def process_response(self, request, response):
                #逻辑判断
                if request.method == 'OPTIONS':
                    response['Access-Control-Allow-Methods'] = 'DELETE,'
                    response['Access-Control-Allow-Headers'] = 'Content-Type'
                response['Access-Control-Allow-Origin'] = '*'
                return response
            
       -配置在配置文件中 配置在中间件
            MIDDLEWARE = [
                'home.middleware.CORSMiddle'
            ]
    	
    
    
    
    
   # 使用第三方解决:
		1.-安装
		pip install django-cors-headers
        2.-注册:添加到setting的app中
        INSTALLED_APPS = (
                ...
                'corsheaders',
                ...
            )
        -加入到中间件
        'corsheaders.middleware.CorsMiddleware',
       	-配置文件配置
        #允许所有域
        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',
        )
上一篇:jQuery响应式幻灯片插件jquery.glide.js(支持触摸&轻量级)


下一篇:Android-RX子可观察-仅执行一次