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单独放在客户端,用浏览器打开,再触发事件,会发现报错:

这是因为浏览器的同源策略导致的。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
那么如何解决这种跨域问题呢,我们主要由三个思路:
- jsonp
- cors
- 前端代理
这里主要给大家介绍第二个: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 = '域名' 或 '*'
支持跨域复杂请求:
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
浙公网安备 33010602011771号