四方显神

导航

Django开发笔记(八)Ajax

接下来的学习就是相对比较轻松的,一些对Django的补充,Ajax是其中最重要的,简单但超级重要,必须学会。

这部分学习发现jQuery忘得很干净,参考:jQuery css() 方法 | 菜鸟教程 (runoob.com) 

客户端(浏览器)向服务端发起请求的形式:

  • 地址栏:GET
  • 超链接标签:GET
  • form表单:GET或POST
  • Ajax(重要):GET或POST或PUT或DELETE

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。Ajax是JS技术,不是Django组件。

AJAX的特点和优点:

  • 异步
  • 局部刷新

 一、json数据

1.python和json数据格式对照:

Python Json 
dict object
list,tuple array
str string
int,float number
True true
False false
None null

2.python中的序列化和反序列化:

from django.test import TestCase

# Create your tests here.
import  json


# 序列化:将数据对象转换为一种标准格式字符串,比如json字符串
data = {"name":"李商隐",'age':18,'dinasty':'唐'} # Python里不区分单双引哦
jsondata = json.dumps(data)
print(jsondata)

data2 =  json.loads(jsondata) # 反序列化
print(data2)

print("^^^^^")
stu = ({'name':'bj'},{'name':'mht'})
jsonstu = json.dumps(stu)
print(jsonstu)
print("^^^反序列化:")
stu2 = json.loads(jsonstu)
print(stu2)

补充知识点:

看到老师反序列化使用的是loads和dumps,不是load和load,没看懂源码,查了一下两者区别,参考:python中,json.load()和json.loads()的区别-CSDN博客

那关于dump和dumps的区别应该也是相对的,参考:Python | 一文简单搞懂json.dump()与json.dumps()的区别_json.dump和json.dumps的区别-CSDN博客

注意:

尝试的时候我发现我新Django项目不是中文编码,将Django项目设置为中文编码只需要在settings.py文件中将编码的两行代码修改一下即可(不是我这个问题的解决方法):

# LANGUAGE_CODE = 'en-us'
#
# TIME_ZONE = 'UTC'

# 把英文改为中文 LANGUAGE_CODE = 'zh-hans' # 把国际时区改为中国时区 TIME_ZONE = 'Asia/Shanghai'

但是并没有解决我乱码的问题,后来参考Python json.dumps() 中文乱码问题_json.dumps encoding-CSDN博客

将json.dumps参数ensure_ascii参数设置为null就可以了。

但是我看老师就没有设置这个参数,可能是他python版本用的不是我这个3.11?为了方便我给我的dumps源码改了参数(本来True改成False),这样就不需要我每次都带参数了,这个也是可行的:

3.Django支持的序列化方法

# 序列化响应类
from django.http import JsonResponse
JsonResponse({})
# 序列化queryset
from django.core import serializers
ret = models.Book.objects.all()
data = serializers.serialize("json", ret)

二、Ajax请求案例

做一个用户输入,光标移开的时候js发一个ajax请求,特定条件下可以显示“用户已经存在”这么个功能。

1.新建一个应用user

建完注意检查settings里INSTALLED_APPS有没有配置这个应用,我这边是没有自动配上的,需要加上:

 

2.models.py里创建一张表,用来存已经存在的用户信息:

from django.db import models

# Create your models here.
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)

    class Meta:
        db_table = "db_users"

3.数据迁移

经典两个语句:

python manage.py makemigrations

python manage.py migrate    

创建完成后将左边的数据库sqlite3拉进右边database里(都是上节课的操作方法和顺序,不贴图了)。

4.给数据库整几条测试数据

准备阶段就结束了。

5.代码(非最终版本)

views.py:

from django.shortcuts import render,HttpResponse
from users.models import User

# Create your views here.



def reg(request):

    return render(request,"reg.html")

def username_auth(request):
    print(request.POST)
    user = request.POST.get("user")
    # 校验用户名是否存在
    ret = User.objects.filter(name = user)
    if ret:
        # 用户名存在
        return HttpResponse("该用户已存在")
    else:
        # 用户名不存在
        return HttpResponse("")

reg.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>

    <!-- 这个是从bootCDN里拿的 -->
    <SCRIPT SRC="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js">
    </SCRIPT>

</head>
<body>
<h3>注册页面</h3>
<form action="" >
    用户名:<input type="text" class="user">
    <span class="err"> </span>

    <script>
        $(".user").blur(function (){
            //发送ajax请求,基于jQuery
            $.ajax({
                {#url:"http://127.0.0.1:8000/users/username_auth",  #}
                url:"/users/username_auth/", //ip和端口与当前页面一致就可以写相对路径
                type:"POST",
                data:{
                    "user":$(".user").val(),
                    "pwd":"123"
                },
                // 当ajax请求成功时响应回来的时候触发的回调函数
                success:function (res){
                    // 通过dom操作将响应渲染导页面中,发生局部变化
                    $(".err").html(res)
                }
            })
        })
    </script>


</form>

</body>
</html>

这个案例的流程图:

6.完善代码(非最终版本)

我们写到现在,没有涉及到json数据。当我们发送ajax的时候一般都是返回json的。

在上图第3步,响应返回的是一个字符串,客户端拿到之后不好处理,它不知道这个字符串说的是成功还是失败。

我们需要修改一下代码,views.py:

def username_auth(request):
    print(request.POST)
    user = request.POST.get("user")
    # 校验用户名是否存在
    res = {"exist":False,"msg":""}
    ret = User.objects.filter(name = user)
    if ret:
        # 用户名存在
        res["exist"] = True
        res["msg"] = "该用户已存在"
    return HttpResponse(json.dumps(res))

reg.html: (使用了JS中的json反序列化方法,和Python中不一样)

 // 当ajax请求成功时响应回来的时候触发的回调函数
                success:function (res){
                    // 修改了这里
                    {#console.log(res)#}
                    {#console.log(JSON.parse(res))#}
                    res = JSON.parse(res)
                    if(res.exist){
                        $(".err").html(res.msg)
                    }else{
                        $(".err").html("")
                    }
                }

7.进一步完善代码(最终版本)

上面我们响应返回用的是HTTPResponse,下面我们使用一个新的JsonResponse,要引一下: from django.http import JsonResponse。

使用这个的好处就是可以不用自己序列化了,还会在响应头里加上一个contentType,表示自己是json类型,加上这个响应头,客户端发现是json会帮我们自动解析,在reg.html页面中就不需要自己进行JSON.parse了。

views.py页面:

def username_auth(request):
    print(request.POST)
    user = request.POST.get("user")
    # 校验用户名是否存在
    res = {"exist":False,"msg":""}
    ret = User.objects.filter(name = user)
    if ret:
        # 用户名存在
        res["exist"] = True
        res["msg"] = "该用户已存在"
    # return HttpResponse(json.dumps(res))
    from django.http import JsonResponse
    # 使用JsonResponse可以不用自己序列化了,同时还会在响应头里加上一个contentType,表示自己是json类型
    # 加上这个响应头,客户端发现是json会帮我们自动解析,在reg.html页面中就不需要自己进行JSON.parse了
    return JsonResponse(res)

reg.html页面:

 // 当ajax请求成功时响应回来的时候触发的回调函数
                success:function (res){
                    // 我们这里就不需要自己反序列化咯
                    if(res.exist){
                        $(".err").html(res.msg)
                    }else{
                        $(".err").html("")
                    }
                }

 

 

三、同源策略

1.同源策略和跨域

现在我们将reg.html单独放在客户端,用浏览器打开,再触发事件,会发现报错:

image20210812115456734

这是因为浏览器的同源策略导致的。

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

那么如何解决这种跨域问题呢,我们主要由三个思路:

  1. jsonp
  2. cors
  3. 前端代理

这里主要给大家介绍第二个:cors

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

所以,服务器方面只要添加一个响应头,同意跨域请求,浏览器也就不再拦截:

response = JsonResponse(data)
response["Access-Control-Allow-Origin"] = "*"

2.cors

cors有两种请求:简单请求和非简单请求

只要同时满足以下两大条件,就属于简单的请求:

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

 

凡是不同时满足上面两个条件,就属于非简单请求。浏览器对这两种请求的处理,是不一样的。

简单请求:一次请求

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

- 请求方式:OPTIONS - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息

- 如何“预检” => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers

支持跨域,简单请求:

服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'

支持跨域复杂请求:

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

cors在Django中的实现:

在返回结果中加入允许信息(简单请求):

def test(request):
    import json
    obj=HttpResponse(json.dumps({'name':'yuan'}))
    # obj['Access-Control-Allow-Origin']='*'
    obj['Access-Control-Allow-Origin']='http://127.0.0.1:8004'
    return obj

放到中间件中处理复杂和简单请求:

from django.utils.deprecation import MiddlewareMixin
class MyCorsMiddle(MiddlewareMixin):
    def process_response(self, request, response):
        # 简单请求:
        # 允许http://127.0.0.1:8001域向我发请求
        # ret['Access-Control-Allow-Origin']='http://127.0.0.1:8001'
        # 允许所有人向我发请求
        response['Access-Control-Allow-Origin'] = '*'
        if request.method == 'OPTIONS':
            # 所有的头信息都允许
            response['Access-Control-Allow-Headers'] = '*'
        return response

在settings中配置即可,在中间件中的位置可以随意放置.

也可以通过第三方组件:pip install django-cors-headers

# (1)
pip install django-cors-headers

# (2)
 INSTALLED_APPS = (
 'corsheaders',
)
  
# (3)
MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'corsheaders.middleware.CorsMiddleware',  # 按顺序
     'django.middleware.common.CommonMiddleware',  # 按顺序
 ]
  
  # 配置白名单
 CORS_ALLOW_CREDENTIALS = True#允许携带cookie
 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',
     'origin',
     'user-agent',
     'x-csrftoken',
     'x-requested-with',
     'Pragma',
 ) 

前端项目设置请求头记得添加到CORS_ALLOW_HEADERS

posted on 2023-12-19 20:34  szdbjooo  阅读(24)  评论(0)    收藏  举报