1.3.13 第十三讲 Ajax的实现(一
1 引言
经过一段时间的学习,我想大家对于 Django 的一些基础的东西已经有所了解,但 Django 本身的内容不仅仅如此,它还在发展中,还有许多的专题是我还没有向大家介绍的。因此,随着我和大家一同地学习,我会继续向大家介绍一些更高级的话题。
随着对于web的了解越来越多,我对于 web 上的开发也越来越有兴趣。的确,在实际的工作中我也发现,现在越来越强调团队的管理,许多事情单纯搞一两个人是很困难的,因此如何提高团队工作的一致性和方便性越来越重要,比如:在我所在的项目组,有一些统计信息需要每个人提供,然后进行汇总。目前还是采用手工的方式,这种方式的确简单,但不能自动地进行管理,也不利于以后的归档处理。因此我很希望做成 web 的应用,让每个人可以自由创建项目,提交数据。但就是这样的一个简单的工作,也不是非常简单的事情。如何快速对 Django 加深了解,如何提高开发效率,如何更有效地利用 web 是我更关心的,而不仅仅是做出一个可用的应用来。这包括一系列的 NewEdit 的扩展,及其关知识的积累。
特别让我感兴趣,并且可以极大的提高用户体验的一种 web 技术就是 Ajax 了。它是什么?它是一种技术的总称,包括了 Html, CSS, XML, Javascript 等与 web 相关技术的合集,在我以前的 Blog 也有一些涉及,但那时关注的焦点不在 web 上。现在有机会和时间好好地了解了一下,特别是在 Django 中已经做为实现的目标正在逐步地开展起来,只不过目前还没有可用的东西呈现出来。
Ajax技术实际上就是利用了浏览器提供的XMLHttpRequest函数(XHR),在不重新加载网页的情况下,可以异步从后台读取数据改变网页内容的一项技术。AJAX即Asynchronous JavaScript and XML(异步JavaScript和XML)
随着近些年前端技术的不断发展,JavaScript也在不断进化。现在我们使用前端库和React、Angular、Vue等框架构建了动态的网站。AJAX的概念也经历了重大变化,因为现代异步JavaScript调用涉及检索JSON而不是XML。有很多库允许你从客户端应用程序对服务器进行异步调用。有些进入到浏览器标准,有些则有很大的用户基础,因为它们不但灵活而且易于使用。有些支持promises,有些则使用回调。
Vue2.0之后,尤雨溪推荐大家用axios替换JQuery ajax,让axios进入了很多人的目光中。axios本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。
下面就让我以 axios 为基础来向大家介绍一下如何在 Django 中使用它,使用一些简单的 Ajax 技术。
首先让我们关心一下 Ajax 与 Django 的关系。其实 Ajax 本身包含许多的内容,它有浏览器端的显示技术,有与后台通讯的处理,因此与 Django 有关系的其实只有与后台交互那块东西。这样,更多的关于前端显示的技术,如:显示特效,这些都属于 CSS, Javascript的内容,而这些与 Python 本身的关系也不大,因此你还需要掌握这些东西才可以做得更好。也许有机会会有专题和学习和介绍这些方面的东西。
下面的试验主要关注的是前端与后端的交互,也就是如何实现浏览器与 Django 交互,体验不进行页面的刷新(这是Ajax最大的好处,一切都好象在本地进行一样)。
就目前来说, Ajax 与后台交互都是通过浏览器提供的 XMLHttpRequest
对象来实现的。这个对象支持同步和异步的调用,但由于 Javascript 本身没有多线程这个东西,因此为了不阻塞浏览器,一般都采用异步方式来调用,而这也是一般的 Ajax 框架提供了默认方式。就目前来说,交互数据也有多种格式,比如:XML, Json , 纯文本/Html。 XML 不用说了,但一般采用 http 协议的 web server 是无法直接支持,因此需要进行转换。同时在浏览器你要同时进行XML的解析,不是非常方便。 Json 是一种数据表示格式,它非常象 Python 的数据类型。而且它只有数据,没有任何的格式,因此数据传输量非常小。再加上处理起来也很方便,在传输上可以直接转换为文本,然后再转换成不同语言的数据结构即可。对于 Python 是非常方便。再有就是文本/Html方式,一种是自定义格式,通过转化为文本进行处理,另一种就是直接使用 html 标记。前一种需要自行做扩展,后一种则是最方便。下面我们将先使用 html 方式,然后再使用 Json 来进行试验。
我设计了一个非常简单的例子:提供一个输入框,用户输入文本,然后点提交,直接在下面显示后台返回的结果。因为我不是 Javascript , CSS 的专家,可能有不对的地方。
2 创建 Ajax 应用
python manage.py startapp ajax
3 修改 ajax/views.py
# Create your views here.
from django.http import HttpResponse
def input(request):
input = request.REQUEST["input"]
return HttpResponse('<p>You input is "%s"</p>' % input)
从这里可以看出,我需要一个 input 字段,然后返回一个HTML的片段。
4 创建 templates/ajax 目录
5 创建 templates/ajax/ajax.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Ajax Test</title>
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.js"></script>
<script src="/static/ajax_test.js"></script>
</head>
<body>
<h1>
Ajax 演示
</h1>
<div>
<form id="form">
输入:<input type="text" id="name" name="input"/>
<input id="submit" type="button" value="提交" />
</form>
</div>
<div id="output"></div>
</body>
</html>
这个模板将作为初始页面,它用来处理向后台发起请求。在这里它没有需要特殊处理的模板变量,只需要显示即可。但在这里的确有许多要说明的东西。
这是一个标准的 html 的页面,在 head 标签中,它将引入三个 js 文件: jquery.js 、 axios.js 和 ajax_test.js 。我们采用jsdelivr所提供的CDN服务加载jquery.js和axios.js,这样有两个好处,一来可以保持我们始终采用最新的版本,二来也可以分担我们服务器的流量压力。从 url 上可以看出,我将会ajax_test.js放在 static 下,这个地址就是 static 目录。
在 html 文件中有一个 form ,它的 id 是 form ,我将用它来查找 form 对象。它有一个文本输入框,还有一个按钮,但这个按钮并不是 submit 按钮。这里有许多与标准的 form 不一样的地方,没有 action, 没有 method ,而且没有 submit 按钮。为什么要这样,为了简单。以前写 HTML,CSS, Javascript 和事件之类的处理,我们一般可能会写在一起,但这样的确很乱。我们在这里尝试使用代码分离技术,而这也是目前可能流行的做法。我们在独立的 Javascript 中编写代码,在装载页面时动态地查找相应的元素,然后设置元素的一些属性,如 style ,事件代码等。而在 Html 文档中,你看到的元素中一般就只有 id , class 等内容。这样的好处可以使得处理为以后重用及优化带来方便,同时可以通过编程的方式实现批量的处理,而且也使得 Html 页面更简单和清晰。因为我要使用 Ajax 去动态提交信息,不需要真正的 form 的提交机制,我只是需要用到 form 元素中的数据而已,因此象 action, method 等内容都没有用。 id 是必须的,我需要根据它找到我想要处理的元素对象。
不过分离的作法是你的文件将增多,也可能不如放在一个文件中便于部署吧。这是一个仁者见仁,智者见智的作法。
<div id="output"></div>
它是用来显示结果的层。
整个处理过程就是:
在装载 html 页面时,会对按钮进行初始化处理,即增加一个 onclick
的事件处理,它将完成 Ajax 的请求及结果返回后的处理。然后用户在页面显示出来后,可以输入文本,点击按钮后,将调用 onclick
方法,然后提交信息到 Django ,由 Django 返回信息,再由 Ajax 的 deferred 对象(后面会介绍)调用显示处理。
6 创建 static/ajax_test.js
function submit(){
axios.get('/ajax/input/', {
params: {
input: $("#name").val()
}
})
.then(function (response) {
// handle success
console.log(response);
$("#output").html(response.data);
$("#output").show();
})
.catch(function (error) {
// handle error
console.log(error);
alert(error);
});
}
function init() {
$("#submit").click(function(){
submit();
});
$("#output").hide();
}
$(function(){
init();
});
首先让我们看 $(function(){});
它表示将 init()
函数加到 window.onload
的响应事件对列中。浏览器在装载完一个页面后,会自动调用 onload
事件处理。因此在这里是进行初始化的最好的地方。
init()
方法一方面完成对 id 名为 submit
的按钮 onclick
处理函数的绑定工作,另一个是将 id 为 output
的元素隐藏。其实不隐藏也无所谓,因为它本来就是空的,因此你也看不到东西。不过如果有其它的东西这样的处理却也不错。
$()
是 jQuery 提供的一个 getElement()
函数别名,它将根据元素的 id 来得到某个对象。
hide()
是隐藏某个元素。想要显示某个元素可以使用 show()
。
最重要的工作都在 submit()
这个函数中。它通过调用 axios 提供的 get()
函数提交一个 Ajax 请求到后台。第一个参数是请求的 url ,第二个如果有的话,应该是 Query String ,即一个 url 的 ? 后面的东西。
在执行了 get()
之后,结果可能当时并没有返回回来,因为这是一个异步调用。因此为了在结果回来之后做后续的处理,我还需要挂接两个异步函数,一个用来处理成功的情况,一个是用来处理失败的情况。 then()
和catch()
就是做这件事的。
then()
在 HTTP GET请求正确返回后会被调用。 response
是一个对象,它有一个 data
属性可以使用。这里因为 Django 返回的是 Html 片段,因此我只是简单地将 output
对象(用于显示的 div 层)的内容进行了设置。然后调用 show()
来将层显示出来。
catch()
则只是调用 alert()
显示出错而已。
这里有许多 Javascript 、jQuery、axios 的东西,如果大家不了解则需要补补课了。
7 创建 ajax/urls.py
增加两行:
from django.conf.urls import url
from django.views.generic import TemplateView
from . import views
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name="ajax/ajax.html")),
url(r'^input/$', views.input),
]
前一个使用了 generic view 所提供的 TemplateView.as_View
方法可以直接显示一个模板。后一个则指向了 views.input()
方法,它用于在前一个页面点击按钮后与后台交互的处理。
8 安装 ajax 应用
修改 settings.py
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'debug_toolbar',
'newtest',
'wiki.apps.WikiConfig',
'address.apps.AddressConfig',
'ajax.apps.AjaxConfig',
)
9 启动 server 测试
这样你在文本框中输入内容,点击提交后就会立即在文本框的下面看到结果,而页面没有刷新,这就是 Ajax 就直接的做用。