Ajax
ajax
可以使当前浏览器不需要整个重新加载,只是局部刷新,给用户的体验良好,并且相对而言效率更高一些
-
同步交互:客户端发出一个请求后,需要等待服务器相应结束后,才可以发起第二个请求
-
异步交互:客户端发出一个请求后,无需等待该次服务器的相应,即可发起第二个请求
什么是json
解决xml
格式复杂的问题,是一个多种语言通用的轻量级数据交换格式;几乎所有语言都可以生成和解析json
格式的数据
- 数据在键值对中
- 数据由逗号分隔
- 花括号存储数据
- 方括号保存数组
python
[
{ "name":"Bill", "age":1 },
{ "name":"George", "age":2 },
{ "name":"Thomas", "age": 3 }
];
- 我们一般通过后段返回
json
数据与前端进行ajax
异步数据通信
Jqery-ajax
使用ajax
进行django
后台数据的异步获取,django
只是提供的数据,不承担前端页面的渲染工程;这里使用jQuery
所提供的ajax
方法进行异步通信
- 首先测试数据库中模型类定义如下:
python
class Article(models.Model):
title = models.CharField(max_length=50,verbose_name="标题")
author = models.CharField(max_length=20,verbose_name="作者")
date = models.DateField(auto_now_add=True,verbose_name="发表日期")
content = models.TextField(verbose_name="文章内容")
def __str__(self):
return self.title
测试数据可由用户自行添加,非常简单
- 编写主页视图函数,返回所有数据库中内容
python
def index(request):
articles = models.Article.objects.all()
return render(request,'ajax/index.html',locals())
此处的index.html
页面不光承担所有数据的渲染工作,还将负责未来ajax
异步请求,获取对应文章的详细内容
index.html
页面代码
css
<style>
label{
border: 5px outset gray;
width: 150px;
margin-top: 10px;
}
</style>
jinja2
<head>
{% load staticfiles %}
<meta charset="utf-8">
<title>Ajax测试</title>
<script type="text/javascript" src="{% static 'js/jquery-1.10.2.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.cookie.js' %}"></script>
<!-- 该js文件用来引入jquery所提供的获取cookie值的库 为了提取对应csrf_token-->
</head>
<body>
<h1>这是一个ajax的请求测试</h1>
{% for article in articles %}
<label class="{{ article.id }}">{{ article.author }}:{{ article.title }}</label>
{% endfor %}
<p class="content"></p>
</body>
js
<script type="text/javascript">
$(document).ready(function () {
$("label").click(function () {
$.ajax({
url: '/article/', // 请求地址,对应Django某个路由映射
type: 'POST', // 请求方式 post
data: {
'csrfmiddlewaretoken': $.cookie('csrftoken'),
// 提交数据需有当前csrf_token 防跨站请求伪造令牌
'id_': $(this).attr('class'),
// 获取当前的id值 传递到视图后台
},
success: function (result) {
var data = JSON.parse(result)
// 解析获得实际字符串
$('.content').html(data)
// 将内容以html形式显示到对应的p标签上
}
})
})
})
</script>
有了前端页面,并且ajax
的请求地址为/article/
那么就需要我们定义一个视图函数返回对应的json
数据,并且设置路由为/article/
python
#urls.py
path('ajax/',ajaxviews.index), # 首页路由
path('article/',ajaxviews.article) # ajax请求路由
python
#views.py
def article(request):
if request.is_ajax(): # 判断是否为ajax请求
if request.method == "POST": # 为ajax的post方式请求
id_ = request.POST.get('id_')
if id_:
try:
content = models.Article.objects.get(id=id_)
content = content.replace('\r\n','<br>')
# 这里还将获取到的文章字符串内容中的换行替换为HTML的换行标签
except models.Article.DoesNotExist:
raise Http404
else:
data = json.dumps(content,ensure_ascii=False,cls=JsonEncoder)
# 返回get对应取到的实际属性
return HttpResponse(data)
raise Http404
这里要注意的是,后端返回的数据得是序列化之后的才可以被前端js
所解析
直接返回一个django model
数据实例是不行的。所以需要我们视图函数对需要返回的数据进行序列化操作
序列化
在django
中原生的对于数据的序列化操作主要有以下两种
Json序列化
普通Python
数据直接使用json
模块进行序列化
python
content = models.Article.objects.get(id=id_).content.replace('\r\n','<br>')
#这里将文章内容对应返回,之所以有replace函数,是因为文章数据是通过admin后台复制添加,需要将其中的\r\n换行转换为HTML可以解析的<br>标识符
data = json.dumps(content,ensure_ascii=False)
# 第二个参数是因为序列化时对中文默认使用的ascii编码,此时需要将该值设置为False,这样前端接收到时才是一个正常中文结果
return HttpResponse(data)
- 注意 :如果要序列化的数据中包含时间类型
date
或datetime
时,这种办法就会报错
python
TypeError: Object of type date is not JSON serializable
python
class JsonEncoder(json.JSONEncoder):
# 自定义json处理器
def default(self, obj):
if isinstance(obj, datetime):
# 如果判断到类型为datetime格式
return obj.strftime('%Y-%m-%d %H:%M:%S')
# 处理为字符串类型的 (年-月-日 时:分:秒)
elif isinstance(obj, date):
# 如果判断到json处理数据为date类型
return obj.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self,obj)
# 其他数据类型按照默认的序列化方式处理即可
使用cls
指定序列化方式,即可轻松解决特殊格式没有办法被json
序列化的问题
python
content = models.Article.objects.get(id=id_).date
data = json.dumps(content,ensure_ascii=False,cls=JsonEncoder)
# 通过json.dumps的cls参数指明所使用的自定义序列化类
return HttpResponse(data)
如果返回的数据并不是一个单独的数据属性,那么也可以通过
json
进行处理,以一个数据列表的形式返回
python
content = models.Article.objects.filter(id=id_).values()
data = json.dumps(list(content),ensure_ascii=False,cls=JsonEncoder)
return HttpResponse(data)
对应前端接收展示
html
<div class="content">
<!-- 这里用到的不是之前的p标签 而是一个div容器 -->
</div>
js
success: function (result) {
var data = JSON.parse(result)
var tag = ''
for (var i = 0, len = data.length; i < len; i++) {
// 如果需要展示的是所有的结果,可以通过js的for循环
tag += '<p>' + data[i]['content'].replace(/\r\n/g, "<br>") + '</p>'
tag += '<hr>'
}
$('.content').html(tag)
}
serializer序列化
serializer
是由django
所提供的一个专门用来处理django
数据对象(django model)
变为序列化数据的框架
这种序列化不支持单个对象 ,比如像objects.get
获取到的数据,或是Python
中的 str
等数据类型
该序列化框架所提供的功能类位于django.core.serializers
python
#views.py
from django.core import serializers
content = models.Article.objects.filter(id=id_)
data = serializers.serialize('json',content,ensure_ascii=False)
return HttpResponse(data)
js
var data = JSON.parse(result)[0]['fields']['content'] // 序列化传输方式
$('.content').html(data.replace(/\r\n/g,"<br>"))
console.log(data)
- 总结 :通过管理器的
get
方法获取到的是一个独立的结果,并不是一个QuerySet
数据对象,也不是一个普通Python
数据类型;只能对数据其中的某条属性进行json
格式的处理或是将其变为列表等序列数据类型之后再进行序列化处理
serializer反序列化
这是一种序列化的反向操作,将json数据转换为序列化之前的样子
看个demo
python
from django.core import serializers
content = models.Article.objects.filter(id=id_) # QuerySet
data = serializers.serialize('json',content,ensure_ascii=False) # str
content = serializers.deserialize("json", data)
return HttpResponse(data)
跨域
浏览器有一个很重要的概念:同源策略(Same-Origin Policy)
所谓同源是指,域名,协议,端口相同
不同源的客户端脚本javascript、ActionScript
在没明确授权的情况下,不能读写对方的资源
- 同源:请求资源的地址与请求的发起方都属于同一域名下
JSONP
JSONP
是 JSON with padding
(填充式 JSON
或参数式 JSON
)的简写
JSONP
实现跨域请求的原理简单的说,就是动态创建<script>
标签,然后利用<script>
的src
不受同源策略约束来跨域获取数据。
JSONP
由两部分组成
-
回调函数:回调函数是当响应到来时应该在页面中调用的函数;回调函数的名字一般是在请求中指定的
-
数据:数据就是传入回调函数中的参数
-
注意 :
JSONP
方式解决AJAX
跨域,必须使用get
方式,并且该方式常在一些数据量级比较小的情况下,因为需要服务端后台构建回调函数带参数的字符串,像是下面这样
python
def index(request):
name = request.GET.get('name') + '哈哈哈哈哈'
callback = request.GET.get('callback')
data = '%s("%s")' % (callback,name)
# 这里以前端生成的回调函数名作为函数名,待返回数据作为参数返回
return HttpResponse(data)
- 前端代码:点击按钮传送表单的值到后台,并由后台处理后追加内容返回,返回的结果展示再
p
标签处
html
<input type='text' id='ajax_data'>
<button>
按钮
</button>
<p id="content"></p>
Ajax
代码,获取当前表单数据,并使用get
方式传递到服务端
js
$(document).ready(function () {
$("button").click(function () {
$.ajax({
url: 'http://127.0.0.1:8000/axios/', // 请求地址,对应Django某个路由映射
type: 'get', // 请求方式 post
dataType: "jsonp", // 指定服务端返回的数据为jsonp格式
data: {
'name': $('#ajax_data').val(),
},
success: function (result) {
console.log(result)
$('#content').html(result)
}
})
})
})
ajax
发起请求,并指定服务端返回数据类型为jsonp
格式- 服务端构建函数包含参数的字符串,为
jsonp
请求发起时,给定的回调参数名,参数为要返回的数据 - 客户端先会调用回调函数,然后会调用
success
回调函数可以接收处理服务端返回的数据success
回调函数是成功返回数据后必定会调用的函数
Cross-Origin
跨域资源共享CORS(Cross-Origin Resource Sharing)
是一种机制,它使用额外的HTTP
头来告诉浏览器,让运行在一个 origin
(domain
) 上的Web
应用被准许 访问来自不同源服务器上的指定的资源
当一个资源从与该资源本身所在的服务器不同的域、协议或端口 请求一个资源时,资源会发起一个跨域 HTTP 请求
- 注意:不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了
实现
CORS
通信的关键是服务器。只要服务器实现了CORS
接口,就可以跨源通信
- 这里需要我们将后端视图函数在接收到请求时,返回结果指明头部信息
python
class Cors(View):
def post(self,request):
#判断是否为ajax请求
name = request.POST.get('name')
response = HttpResponse(json.dumps('OK'))
response["Access-Control-Allow-Origin"] = "http://127.0.0.1:5500"
# 允许可以跨域请求的站点
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
# 允许可以跨域访问的请求方式
response["Access-Control-Allow-Headers"] = "*"
# 允许可以跨域请求时的头部字段
return response
- 前端页面的
ajax
代码正常提交数据即可
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
</head>
<body>
<input type="text" id='name'>
<button id='button'>提交</button>
</body>
<script>
$('#button').click(function (){
$.ajax({
url: 'http://127.0.0.1:8000/',
type: 'post',
data: {
name: $('#name').val()
},
success: function(result){
console.log(result)
}
})
})
</script>
</html>
django-cors-headers
除了以上手动构建返回结果的头部信息用来解决跨域问题
在django
中还可以通过一个先成可以自动添加CORS-Header
的中间件,只需要在settings.py
中做一些简单的配置即可
- 使用该中间件需要安装
django
的三方插件
shell
pip install django-cors-headers
- 安装完成之后,在
django
的settings
文件中加载app
python
# settings.py
INSTALLED_APPS = [
...
'django.contrib.staticfiles',
'corsheaders',
]
- 接下来在中间件配置部分加载该插件所提供的中间件
python
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # 顺序需要在common组件之前
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- 继续配置允许跨站请求的白名单设置等属性
python
# settings.py
CORS_ORIGIN_ALLOW_ALL = False # 是否允许其他所有站点发起跨站请求
CORS_ORIGIN_WHITELIST = (
'http://127.0.0.1:5500',
) # 跨站请求白名单
CORS_ALLOW_METHODS = (
'POST',
) # 允许跨站访问的请求方式
CORS_ALLOW_HEADERS = (
'*',
) # 允许跨站请求头中的字段类型
- 注:其中某些设置的默认值为如下所示
python
default_headers = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
)
default_methods = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)