昨日内容回顾

  1. 1. 为什么要做前后端分离?
  2. - 前后端交给不同的人来编写,职责划分明确。方便快速开发
  3. - 针对pc,手机,ipad,微信,支付宝... 使用同一个接口
  4. 2. 简述http协议?
  5. - 基于socket
  6. - 数据格式:
  7. "GET /index?name=123&age=19 http1.1\r\nhost:www.luffyciti.com\r\ncontent-type:application/json...\r\n\r\n"
  8. "POST /index http1.1\r\nhost:www.luffyciti.com\r\ncontent-type:application/json...\r\n\r\n{name:'alex',age:18}"
  9. "POST /index http1.1\r\nhost:www.luffyciti.com\r\ncontent-type:application/enform.....\r\n\r\nname=alex&age=18&xx=19"
  10. - 无状态短链接
  11. 一次请求一次响应之后断开连接
  12. 3. 简述restful 规范?
  13. https://www.luffycity.com/api/v1/courses/?sub_category=0
  14. 看上面一段url,可以说出5
  15. 1. 使用https代替http 2.URL中体现自己写的是API 3. URL中体现版本 4. 使用名词 5.参数要合理
  16. 之后,请求方式,响应信息。可以说后面5
  17. 6. 根据请求方式不同,处理不同的操作 7.
  18. 4. django rest framework组件的作用?
  19. - 快速实现restful 规范
  20. 5. 列举django rest framework组件(10)?
  21. 6. 路飞的表结构

一、restful 接口

根据昨天代码,继续编写。

或者下载代码:

https://github.com/987334176/luffycity/archive/v1.2.zip

注意:删除api目录下的views.py文件,它没有用了

下载数据库使用:

https://github.com/987334176/luffycity/blob/master/db.sqlite3

第一种方式

修改api_urls.py

  1. from django.conf.urls import url
  2. from api.views import course,degreecourse
  3. urlpatterns = [
  4. # url(r'login/$', views.LoginView.as_view()),
  5. url(r'courses/$',course.CoursesView.as_view()),
  6. url(r'courses/(?P<pk>\d+)/$',course.CourseDetailView.as_view()),
  7. url(r'degreecourse/$',degreecourse.DegreeCourseView.as_view()),
  8. url(r'degreecourse/teachers/$',degreecourse.DegreeCourseTeachersView.as_view()),
  9. url(r'degreecourse/scholarship/$',degreecourse.DegreeCourseScholarshipView.as_view()),
  10. ]

修改views目录下的course.py

  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from api import models
  4. from api.serializers.course import CourseModelSerializer
  5. from api.utils.response import BaseResponse
  6. from api.utils.serialization_general import SerializedData
  7. class CoursesView(APIView):
  8. def get(self, request, *args, **kwargs):
  9. """
  10. 查询所有
  11. :param request:
  12. :param args:
  13. :param kwargs:
  14. :return:
  15. """
  16. queryset = models.Course.objects.all()
  17. serializer_class = CourseModelSerializer
  18. data = SerializedData(request, queryset, serializer_class).get_data()
  19. return Response(data)
  20. def post(self, request, *args, **kwargs):
  21. """
  22. 增加一条
  23. :param request:
  24. :param args:
  25. :param kwargs:
  26. :return:
  27. """
  28. class CourseDetailView(APIView):
  29. def get(self, request, pk,*args, **kwargs):
  30. """
  31. 查询单个
  32. :param request:
  33. :param pk:
  34. :param args:
  35. :param kwargs:
  36. :return:
  37. """
  38. def put(self, request, pk,*args, **kwargs):
  39. """
  40. 修改单个
  41. :param request:
  42. :param pk:
  43. :param args:
  44. :param kwargs:
  45. :return:
  46. """
  47. def delete(self, request, pk,*args, **kwargs):
  48. """
  49. 删除单个
  50. :param request:
  51. :param pk:
  52. :param args:
  53. :param kwargs:
  54. :return:
  55. """

上面的方式,更趋近于原始

第二种方式(推荐)

django-rst-framework为我们提供了ViewSet类, ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身. ViewSet类与View类几乎是相同的, 但提供的是read或update, 而不是http动作get或put.

ViewSet类在实例化后, 通过Router类, 最终将URL与ViewSet方法绑定

ViewSet类的父类ViewSetMixin,其实重写了as_view方法

  1. @classonlymethod
  2. def as_view(cls, actions=None, **initkwargs):
  3. """
  4. Because of the way class based views create a closure around the
  5. instantiated view, we need to totally reimplement `.as_view`,
  6. and slightly modify the view function that is created and returned.
  7. """
  8. ...

那么url方式也发生了变化

修改api_urls.py

  1. from django.conf.urls import url
  2. from api.views import course
  3. urlpatterns = [
  4. url(r'courses/$', course.CoursesView.as_view({'get': 'list', 'post': 'create'})),
  5. url(r'courses/(?P<pk>\d+)/$', course.CoursesView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
  6. ]

解释:看这一段{‘get’:’list’,’post’:’create’}

它表示get请求时,转发到list方法。post请求转发到create方法。

这里面定义的方法名,都是约定俗成的。推荐使用这些名字!也可以自定义。

修改views目录下的course.py

  1. from rest_framework.views import APIView
  2. from rest_framework.viewsets import ViewSetMixin
  3. from rest_framework.response import Response
  4. from api import models
  5. from api.serializers.course import CourseModelSerializer
  6. from api.utils.response import BaseResponse
  7. from api.utils.serialization_general import SerializedData
  8. class CoursesView(ViewSetMixin,APIView):
  9. def list(self, request, *args, **kwargs):
  10. """
  11. 查询所有
  12. :param request:
  13. :param args:
  14. :param kwargs:
  15. :return:
  16. """
  17. queryset = models.Course.objects.all()
  18. serializer_class = CourseModelSerializer
  19. data = SerializedData(request, queryset, serializer_class).get_data()
  20. return Response(data)
  21. def create(self, request, *args, **kwargs):
  22. """
  23. 增加一条
  24. :param request:
  25. :param args:
  26. :param kwargs:
  27. :return:
  28. """
  29. class CourseDetailView(APIView):
  30. def retrieve(self, request, pk,*args, **kwargs):
  31. """
  32. 查询单个
  33. :param request:
  34. :param pk:
  35. :param args:
  36. :param kwargs:
  37. :return:
  38. """
  39. def update(self, request, pk,*args, **kwargs):
  40. """
  41. 修改单个
  42. :param request:
  43. :param pk:
  44. :param args:
  45. :param kwargs:
  46. :return:
  47. """
  48. def destroy(self, request, pk,*args, **kwargs):
  49. """
  50. 删除单个
  51. :param request:
  52. :param pk:
  53. :param args:
  54. :param kwargs:
  55. :return:
  56. """

以上方式适用于:用户请求处理时业务逻辑复杂的情况。

注意:如果继承了GenericViewSet,并且返回序列化数据使用了Response

必须要定义queryset属性,否则报错

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图1

看源码

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图2

必须要定义queryset变量,否则执行assert(断言)。

还有一种解决方式,使用JSONRenderer。它也可以返回json数据,但是没有像Response那样,有好看的页面!

三、DRF跨域(cors组件)

运行的vue项目,发现出现跨域

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图3

查看Pycharm控制台输出:

  1. [18/Aug/2018 20:14:31] "GET /api/v1/courses/ HTTP/1.1" 200 212

可以发现,请求实际已经到达了后端。并且做了正确的返回,但是浏览器拒绝接收!

这为什么呢?这是因为浏览器的同源策略

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图4

1.什么是同源策略

同源: 页面地址拥有相同的协议,域名,以及端口号。

同源策略: 页面A中的脚本不能访问不同源的页面B的cookie、localStorage以及IndexDB,也不能获取页面B的DOM信息, 另外在使用AJAX时也会受到相关限制。

2.同源策略的目的

主要是出于安全方面的考虑。现在的网页都用cookie来进行身份验证,如果不限制读取,网页B里的恶意脚本代码可以随意模仿真实用户进行操作。

关于更多的同源策略信息,请参考链接:

http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

总结:

浏览器具有同源策略,打开某个网站后,通过ajax向另外一个网站发送http请求时候,数据回来时会被浏览器阻止(跨域)

解决跨域

方式一

在api端设置响应头 CORS

修改views目录下的course.py,增加响应头

  1. from rest_framework.views import APIView
  2. from rest_framework.viewsets import ViewSetMixin
  3. from rest_framework.response import Response
  4. from api import models
  5. from api.serializers.course import CourseModelSerializer
  6. from api.utils.response import BaseResponse
  7. from api.utils.serialization_general import SerializedData
  8. class CoursesView(ViewSetMixin,APIView):
  9. def list(self, request, *args, **kwargs):
  10. """
  11. 查询所有
  12. :param request:
  13. :param args:
  14. :param kwargs:
  15. :return:
  16. """
  17. queryset = models.Course.objects.all()
  18. serializer_class = CourseModelSerializer
  19. data = SerializedData(request, queryset, serializer_class).get_data()
  20. # 增加响应头,http://localhost:8080表示前端的地址
  21. return Response(data,headers={'Access-Control-Allow-Origin': 'http://localhost:8080'})
  22. def create(self, request, *args, **kwargs):
  23. """
  24. 增加一条
  25. :param request:
  26. :param args:
  27. :param kwargs:
  28. :return:
  29. """
  30. class CourseDetailView(APIView):
  31. def retrieve(self, request, pk,*args, **kwargs):
  32. """
  33. 查询单个
  34. :param request:
  35. :param pk:
  36. :param args:
  37. :param kwargs:
  38. :return:
  39. """
  40. def update(self, request, pk,*args, **kwargs):
  41. """
  42. 修改单个
  43. :param request:
  44. :param pk:
  45. :param args:
  46. :param kwargs:
  47. :return:
  48. """
  49. def destroy(self, request, pk,*args, **kwargs):
  50. """
  51. 删除单个
  52. :param request:
  53. :param pk:
  54. :param args:
  55. :param kwargs:
  56. :return:
  57. """

Access-Control-Allow-Origin字段,表示http://localhost:8080可以请求数据。该字段也可以设为星号,表示同意任意跨源请求

客户端浏览器检查自己的域是否在允许列表中,决定是否处理响应

刷新vue页面,发现没有报错了。说明接收了数据!

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图5

上面是简单请求

简单请求 OR 非简单请求

条件:

  1. 1、请求方式:HEADGETPOST
  2. 2、请求头信息:
  3. Accept
  4. Accept-Language
  5. Content-Language
  6. Last-Event-ID
  7. Content-Type 对应的值是以下三个中的任意一个
  8. application/x-www-form-urlencoded
  9. multipart/form-data
  10. text/plain

注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

简单请求和非简单请求的区别

  1. 简单请求:一次请求
  2. 非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用

关于”预检”

  1. - 请求方式:OPTIONS
  2. - "预检"其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
  3. - 如何"预检"
  4. => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则"预检"不通过
  5. Access-Control-Request-Method
  6. => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则"预检"不通过
  7. Access-Control-Request-Headers text/plain

数据展示

修改settings.py,将分页改成20

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
  3. 'VERSION_PARAM':'version',
  4. 'DEFAULT_VERSION':'v1',
  5. 'ALLOWED_VERSIONS':['v1','v2'],
  6. 'PAGE_SIZE':20,
  7. 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
  8. }

修改vue项目的HelloWorld.vue

  1. <template>
  2. <div class="hello">
  3. <h1>{{ msg }}</h1>
  4. <h1>课程列表</h1>
  5. <ul v-for="item in courseList">
  6. <li>{{item.name}}</li>
  7. </ul>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. name: 'HelloWorld',
  13. data () {
  14. return {
  15. msg: '欢迎使用路飞学城',
  16. courseList:[],
  17. }
  18. },
  19. mounted(){ //页面加载完成后
  20. this.initCourse(); //执行此方法
  21. },
  22. methods:{
  23. initCourse:function () {
  24. let that = this
  25. //向后台发送ajax请求
  26. this.$axios.request({
  27. url:'http://127.0.0.1:8000/api/v1/courses/',
  28. method:'GET',
  29. responseType:'json',
  30. }).then(function (arg) {
  31. //成功之后
  32. console.log(arg);
  33. if (arg.data.code == 1000){
  34. //更新数据
  35. that.courseList = arg.data.data;
  36. }else {
  37. //弹出错误
  38. alert(arg.data.error);
  39. }
  40. }).catch(function (err) {
  41. //发生错误
  42. console.log(err);
  43. })
  44. }
  45. },
  46. }
  47. </script>
  48. <!-- Add "scoped" attribute to limit CSS to this component only -->
  49. <style scoped>
  50. h1, h2 {
  51. font-weight: normal;
  52. }
  53. ul {
  54. list-style-type: none;
  55. padding: 0;
  56. }
  57. li {
  58. display: inline-block;
  59. margin: 0 10px;
  60. }
  61. a {
  62. color: #42b983;
  63. }
  64. </style>

刷新网页,效果如下:

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图6

第二种

使用自定义中间件

修改views目录下的course.py,去掉headers

  1. def list(self, request, *args, **kwargs):
  2. """
  3. 查询所有
  4. :param request:
  5. :param args:
  6. :param kwargs:
  7. :return:
  8. """
  9. queryset = models.Course.objects.all()
  10. serializer_class = CourseModelSerializer
  11. data = SerializedData(request, queryset, serializer_class).get_data()
  12. return Response(data)

在api目录下创建md文件夹,在里面创建cors.py

  1. from django.utils.deprecation import MiddlewareMixin
  2. from django.conf import settings
  3. class CorsMiddleware(MiddlewareMixin):
  4. def process_response(self,request,response):
  5. # 设置响应头
  6. response['Access-Control-Allow-Origin'] = 'http://localhost:8080'
  7. return response

修改settings.py,注册自定义中间件

  1. MIDDLEWARE = [
  2. 'django.middleware.security.SecurityMiddleware',
  3. 'django.contrib.sessions.middleware.SessionMiddleware',
  4. 'django.middleware.common.CommonMiddleware',
  5. 'django.middleware.csrf.CsrfViewMiddleware',
  6. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  7. 'django.contrib.messages.middleware.MessageMiddleware',
  8. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  9. 'api.md.cors.CorsMiddleware',
  10. ]

刷新vue页面,效果同上!

模拟复杂请求

修改HelloWorld.vue,增加登录按钮。发送固定的用户名和密码,指定数据格式为json

  1. <template>
  2. <div class="hello">
  3. <h1>{{ msg }}</h1>
  4. <button v-on:click="doLogin">登录</button>
  5. <h1>课程列表</h1>
  6. <ul v-for="item in courseList">
  7. <li>{{item.name}}</li>
  8. </ul>
  9. </div>
  10. </template>
  11. <script>
  12. export default {
  13. name: 'HelloWorld',
  14. data () {
  15. return {
  16. msg: '欢迎使用路飞学城',
  17. courseList:[],
  18. }
  19. },
  20. mounted(){ //页面加载完成后
  21. this.initCourse(); //执行此方法
  22. },
  23. methods:{
  24. initCourse:function () {
  25. let that = this
  26. //向后台发送ajax请求
  27. this.$axios.request({
  28. url:'http://127.0.0.1:8000/api/v1/courses/',
  29. method:'GET',
  30. responseType:'json',
  31. }).then(function (arg) {
  32. //成功之后
  33. console.log(arg);
  34. if (arg.data.code == 1000){
  35. //更新数据
  36. that.courseList = arg.data.data;
  37. }else {
  38. //弹出错误
  39. alert(arg.data.error);
  40. }
  41. }).catch(function (err) {
  42. //发生错误
  43. console.log(err);
  44. })
  45. },
  46. doLogin(){
  47. this.$axios.request({
  48. url:'http://127.0.0.1:8000/api/v1/auth/',
  49. method:'POST',
  50. data:{
  51. //用户名和密码
  52. user:'xiao',
  53. pwd:'123',
  54. },
  55. //增加headers
  56. headers:{
  57. //指定数据格式
  58. 'Content-Type':'application/json',
  59. },
  60. //响应格式为json
  61. responseType:'json',
  62. }).then(function (arg) {
  63. //成功之后
  64. console.log(arg);
  65. }).catch(function (err) {
  66. //发生错误
  67. console.log(err);
  68. })
  69. }
  70. },
  71. }
  72. </script>
  73. <!-- Add "scoped" attribute to limit CSS to this component only -->
  74. <style scoped>
  75. h1, h2 {
  76. font-weight: normal;
  77. }
  78. ul {
  79. list-style-type: none;
  80. padding: 0;
  81. }
  82. li {
  83. display: inline-block;
  84. margin: 0 10px;
  85. }
  86. a {
  87. color: #42b983;
  88. }
  89. </style>

修改api_urls.py,增加路径

  1. from django.conf.urls import url
  2. from api.views import course,auth
  3. urlpatterns = [
  4. url(r'auth/$', auth.AuthView.as_view({'post': 'login'})),
  5. url(r'courses/$', course.CoursesView.as_view({'get': 'list', 'post': 'create'})),
  6. url(r'courses/(?P<pk>\d+)/$', course.CoursesView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
  7. ]

在views目录下,创建文件auth.py

from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response

from api import models
from api.serializers.course import CourseModelSerializer

from api.utils.response import BaseResponse
from api.utils.serialization_general import SerializedData


class AuthView(ViewSetMixin,APIView):
    def login(self, request, *args, **kwargs):
        print('用户发了POST请求了',request)
        return Response({'code':'ok'})

刷新vue页面,点击登录

查看Console,提示对方不允许

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图7

查看Network,它是一个OPTIONS请求

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图8

查看Pycharm控制台输出:

[18/Aug/2018 21:37:04] "OPTIONS /api/v1/auth/ HTTP/1.1" 200 163

通过以上信息,可以看出。浏览器发送OPTIONS请求,也就是在预检。但是后端没有同意,所以浏览器没有发送POST请求。

怎么才能让它通过预检呢?

修改md目录下的cors,做一个if判断,允许一下!

from django.utils.deprecation import MiddlewareMixin
from django.conf import settings

class CorsMiddleware(MiddlewareMixin):

    def process_response(self,request,response):
        # 设置响应头
        response['Access-Control-Allow-Origin'] = 'http://localhost:8080'
        # 当为OPTIONS时
        if request.method == "OPTIONS":
            # 允许通过的请求方式
            response["Access-Control-Allow-Methods"] = "POST,PUT,DELETE"
            # 允许通过的请求头
            response["Access-Control-Allow-Headers"] = "Content-Type"

        return response

刷新页面,再次点击登录

查看Console,发现请求正常

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图9

查看Network,发现有2次请求

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图10

查看第一个请求,它是OPTIONS请求方式Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图11

查看第二个请求,它是POST请求方式

Day100 restful 接口,DRF组件,DRF跨域(cors组件) - 图12

查看Pycharm控制台输出:

[18/Aug/2018 21:43:24] "GET /api/v1/courses/ HTTP/1.1" 200 394
[18/Aug/2018 21:43:32] "OPTIONS /api/v1/auth/ HTTP/1.1" 200 163
用户发了POST请求了 <rest_framework.request.Request object at 0x0000024630CD85C0>
[18/Aug/2018 21:43:32] "POST /api/v1/auth/ HTTP/1.1" 200 13

通过以上信息,可以看到。数据接收正常!

通过这样,无论是简单,还是复杂,都允许。

django cors组件

CORS,全称为跨域资源共享。它允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。

上面的cors.py,帮我们实现了cors。这个手写的,但是官方提供了cors组件,专门用来解决跨域问题的!

1.安装django-cors-headers

pip install django-cors-headers

2.配置settings.py文件

INSTALLED_APPS = [
    ...
    'corsheaders',  # 注册应用cors
 ] 

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware', # 注册组件cors

)
#跨域增加忽略
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
    '*'
)

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',
)

注释掉自定义组件,settings.py完整代码如下:

"""
Django settings for s11luffycity project.

Generated by 'django-admin startproject' using Django 1.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '-i@r%a=tf*0n6!kzd=m#gx9g82i7@!x=n9jx=jta&(7%zw67#!'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'api.apps.ApiConfig',
    'rest_framework',
    'corsheaders',  # 注册应用cors
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # 'api.md.cors.CorsMiddleware',
    'corsheaders.middleware.CorsMiddleware',  # 注册组件cors
]

ROOT_URLCONF = 's11luffycity.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 's11luffycity.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
    'VERSION_PARAM':'version',
    'DEFAULT_VERSION':'v1',
    'ALLOWED_VERSIONS':['v1','v2'],
    'PAGE_SIZE':20,
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
}

#跨域增加忽略
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
    '*'
)

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',
)

重启django项目,刷新页面,重启登录。效果同上!

OK!问题解决!

本文参考:

https://blog.csdn.net/apple9005/article/details/54427902