一. ajax初识

1. 前后端传输数据编码格式contentType

  使用form表单向后端提交数据时,必须将form表单的method由默认的get改为post,如果提交的数据中包含文件,还要将form表单的enctype由默认的"application/x-www-form-urlencoded"修改为"multipart/form-data"。

  我们可以通过谷歌浏览器-》检查 中的Network查看网络请求的详细信息。

  以form表单为例,其中代码如下(用Bootstrap装饰了一下):

  输入用户名密码,然后随便选一个文件点提交:

  点击view source查看原生数据:

  随后发现后端能拿到文件,不过只是文件名而已:

  随后修改将enctype修改为"multipart/form-data",然后再次提交该文件:

  

  此时原生数据中file看不到了,不过后端可以看到request.FILES中收到了真实的文件,使用GET可以拿到对应的文件对象。

2. 小结

  前后端传输数据编码格式contentType:

  1. application/x-www-form-urlencoded

  • 对应的数据格式:name=jason&password=666&myfile=test.py
  • 后端获取数据:request.POST

  2. multipart/form-data

  • 对应的数据格式:name=jason&password=666
  • 后端获取文件格式数据:request.FILES
  • 后端获取普通键值对数据:request.POST

   注意:django会将urlencoded编码的数据解析自动放到request.POST,即使修改了编码格式,只要有其中有普通的键值对,都能通过request.POST取到数据。

二. ajax的简单使用

  前端朝后端发送请求的方式有四种:

  1. 浏览器窗口手动输入网址(get请求)
  2. a标签的href属性(get请求)
  3. form表单(get、post请求,默认是get请求)
  4. ajax(get、post请求)

  前面三种我们都已经接触过了,接下来来看看第四种ajax。

  • AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互(客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。),传输的数据为XML(当然,传输的数据不只是XML)。
  • AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
  • AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
  • AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

2.1 ajax的基本语法

  ajax主要由四个部分组成:

$('#d1').click(function () {
$.ajax({
// 提交的地址,不写默认提交至当前页面,同form表单的action
url:'/index/',
// 提交的方式
type:'post',
// 提交的数据,一般以键值对的形式出现
data:{'name':'jason','password':'123'},
// 回调函数
success:function (data) { // data接收的就是异步提交返回的结果
alert(data)
}
})
})

   注意:ajax传输数据的默认编码格式也是urlencoded。前后端传输数据时,数据格式与编码要一一对应,比如传输文件就要将编码改为formdata。

2.2 ajax实现前端数字相加

  假设有三个input框,需求是前两个input框输入数字,点击提交按钮后将结果显示在第三个input中,中途页面不刷新且加法运算要通过后端实现。

  大致思路如下:

  1. 拿到input框中输入的数字
  2. 将数字传至后端进行运算
  3. 后端将运算的结果返回给前端
  4. 前端将结果展示在第三个input框中

  首先假设用户输入的均为数字,所以我们不做任何检验。然后ajax是异步JavaScript和XML,JQuery内部封装了JavaScript,我们这里使用JQuery的语法来获取input框中输入内容(JQuery_obj.val()),然后使用ajax的回调函数seccess来将加法运算的结果显示在第三个input框中(JQuery_obj.val(result))。

  代码如下(前端未做任何装饰,可能有点丑):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test1</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<input type="text" id="s1">+<input type="text" id="s2">=<input type="text" id="s3">
<button id="b1">提交</button> <script>
$('#b1').on('click', function () {
$.ajax({
url: '/test1/', //不写默认提交至当前页面
type: 'post',
data:{'s1':$('#s1').val(),'s2':$('#s2').val()},
success:function (data) {
$('#s3').val(data)
}
})
})
</script>
</body>
</html>

html代码

def test1(request):
if request.method == 'POST':
print(request.POST)
s1 = request.POST.get('s1')
s2 = request.POST.get('s2')
# 拿到的前端数据均为字符串格式,所以需要类型转换
res = int(s1) + int(s2)
return HttpResponse(res)
return render(request, 'test1.html')

相应的视图函数

2.2 ajax传输JSON数据

  各编程语言与前端数据传输通常使用json格式,因为json支持多种语言,各编程语言都有相应的json语法。在python中是使用json.dumps()和json.loads()分别实现对象的序列化和反序列化。而JavaScript中是使用JSON.stringify()和JSON.parse()分别实现对象的序列化和反序列化。

  向后端传输JSON数据时,需要修改编码类型,不然会出现以下情况:

  以上情况的出现是因为编码与数据格式不匹配造成的,你传输的是JSON格式字符串,而编码urlencode却让后端拿到的是字典,所以就直接把你的JSON字符串当初字典的key,value拿个空来自己造一个字典出来了。

  为了统一编码和数据格式,需要将编码设置为‘application/json’。

  代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test1</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<button id="b1">提交</button>
<script>
$('#b1').on('click', function () {
$.ajax({
url:'',
type:'post',
data:JSON.stringify({'name': 'json', 'password': '123'}),
contentType:'application/json',
success:function (data) {
alert(data)
}
})
})
</script>
</body>
</html>

html代码

def test1(request):
if request.method == 'POST':
import json
# 编码改成application/json之后,传输过来的是二进制数据,存在request.body中
dic = json.loads(request.body.decode('utf-8'))
print(dic, type(dic))
return HttpResponse('get it')
return render(request, 'test1.html')

视图函数

2.3 ajax传输文件

  传输文件相比传输json字符串要复杂一些,首先要想办法把用户上传的文件取出来,这需要用到JavaScript中的FormData对象的方法,其次还是要统一数据格式与编码,将编码改为false(因为formdata内部有自带一个编码)。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test1</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
file:<input type="file" name="myfile" id="f1">
<button id="b1">提交</button>
<script>
$('#b1').on('click', function () {
let formdata = new FormData();//生成一个FormData对象
//$('#f1')[0]得到的是JQuery对象中的第一个JS对象,JS_obj.files拿到所有的文件,通过索引取第一个文件
formdata.append('file', $('#f1')[0].files[0]);
$.ajax({
url:'',
type:'post',
contentType:false,//用FormData自带的编码,所以不需要设置contentType
processData:false,//告诉浏览器不要处理数据
data: formdata,//直接将formdata提交至后端即可
success:function (data) {
alert(data)
}
})
})
</script>
</body>
</html>

html代码

def test1(request):
if request.method == 'POST':
# 没有普通键值对,所以request.POST是空
print(request.POST)
print(request.FILES)
# request.FIlES.get('file')拿出来的是文件对象
file_obj = request.FILES.get('file')
# 保存文件至本地
with open(file_obj.name, 'wb') as f:
for line in file_obj:
f.write(line)
return HttpResponse('get it')
return render(request, 'test1.html')

对应视图函数

2.4 ajax小试验

  初次接触ajax,对ajax理解只是一点点皮毛,今天受一个朋友影响,对ajax的理解加深了一点点,在此感谢CC teacher。

  接下来说的方法可能有点鸡肋,可是又有点意思。拿图书管理系统举例,点击添加作者,直接在当前页面局部刷新来实现,不用标签的隐藏等各种方法,就只用ajax来实现(就是那么头铁)。

  大致思路:

  1. 前端页面展示作者数据的区域用一个div标签包裹
  2. 点击添加按钮,利用click事件和ajax,从后端获取一个用render渲染过的HTML页面(该页面需要啥就写啥,不需要body和head标签,同模板导入与自定义inclusion_tag一样)。
  3. 将获取的页面data通过JQ语法.html(data)显示在div中(这时原本显示作者数据的区域数据就会全被获取的页面覆盖了)。
{% extends 'home.html' %}
{% block content %} <div id="d1">
<button id="i1" class="btn btn-info">添加</button>
<table class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>gender</th>
<th>phone</th>
<th>addr</th>
<th>action</th>
</tr>
</thead>
<tbody> {% for author in author_list %}
<tr>
<td>{{ author.pk }}</td>
<td>{{ author.name }}</td>
<td>{{ author.age }}</td>
<td>{{ author.get_gender_display }}</td>
<td>{{ author.authordetail.phone }}</td>
<td>{{ author.authordetail.addr }}</td>
<td>
<a href="{% url 'author_edit' author.pk %}" class="btn btn-success">编辑</a>
<a href="{% url 'author_delete' author.pk %}" class="btn btn-danger">删除</a>
</td>
</tr>
{% endfor %} </tbody>
</table>
</div>
{% endblock %} {% block js %}
<script>
$('#i1').click(function () {
$.ajax({
url: '{% url "author_add" %}',
type: 'post',
data: {'type': '333'},
success: function (data) {
//将id为d1的div标签中的html换成data变量中存的数据
$('#d1').html(data)
}
})
})
</script>
{% endblock %}

大致代码

  form表单和ajax是可以一起使用的,不过这样感觉没什么意义,试验了一下,并没有出现网上说的错误。ajax绑定form表单中的button按钮及submit按钮,两者效果一样,后端先获取到ajax提交的数据,然后再获取到from表单中的数据,有兴趣可以自行试验。

  注意:虽然ajax是异步提交(GitHub注册示例)、局部刷新,但是并不是所有ajax使用的越多越好,因为ajax异步的回调函数会向后端询问执行的结果,当同时有很多该请求时,服务端会有很大的负担。

三. 批量插入,分页器的简单实现

   先在数据库中创建多条数据,然后展示在页面上,这里以1000条为例。

def test1(request):
for i in range(100):
models.Book2.objects.create(name='第%s本' % i)
book_list = models.Book2.objects.all()
return render(request, 'test1.html', locals())

  以上这么增加数据时,发现要在页面等一段时间才会有数据显示,因为写入数据库要时间,前端只能等待数据写入数据库结束。这种情况需要用到批量插入bulk_create:

def test1(request):
# 定义一个列表
book_list = []
for i in range(100):
# 实例化出Book2的对象,并将其加入列表
book_list.append(models.Book2(name='第%s本' % i))
# 这就是批量导入的精髓,相当于异步,程序执行无需等待该代码执行完毕,可直接去执行后续代码
models.Book2.objects.bulk_create(book_list)
# 随后前端直接可以使用book_list中的书籍对象点属性去展示内容,不需要等数据库中数据写入完毕
return render(request, 'test1.html', locals())

  上述使用的是列表,但是当列表中数据很多时,会占用很多的内存,可以采用生成器的方式来进行优化:g = (models.book(name = '第%s本' % i for i in range(10000)))。

  分页器是需要考虑一共有几页的,这需要依据数据总条数来定,其次分页器每一次只显示几个按钮,这就意味着我们是没法在前端来完成这个动态的过程的,只能在后端完成,这时候需要用到后端或者前端的取消转义语法。

html = ''
for i in range(1,pager_nums+1):
html += '<li><a href="?page=%s">%s</a></li>'%(i,i)

  最终代码如下:

class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数 用法:
queryset = model.objects.all()
page_obj = Pagination(current_page,all_count)
page_data = queryset[page_obj.start:page_obj.end]
获取数据用page_data而不再使用原始的queryset
获取前端分页样式用page_obj.page_html
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1 if current_page < 1:
current_page = 1 self.current_page = current_page self.all_count = all_count
self.per_page_num = per_page_num # 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2) @property
def start(self):
return (self.current_page - 1) * self.per_page_num @property
def end(self):
return self.current_page * self.per_page_num def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1 # 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1 page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
page_html_list.append(first_page) if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp) if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page) last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)

分页器代码

  先在应用下新建一个叫utils的文件夹,里面创建一个.py文件,将该代码拷贝进去保存。然后将该py文件导入到views.py中。

from django.shortcuts import render,HttpResponse
from app01 import models
# 导入拷贝了上述分页器代码的py文件
from app01.utils import my_page def booklist(request):
book_list = models.Book2.objects.all()
# 拿到数据总条数
all_count = book_list.count()
# 得到当前页面,前端的分页器被点击时返回page,标明用户点击的页编号
current_page = request.GET.get('page',1)
page_obj = my_page.Pagination(current_page=current_page,all_count=all_count)
# 将总数据按用户点击的页编号切片
page_queryset = book_list[page_obj.start:page_obj.end]
return render(request,'booklist.html',locals())

最新文章

  1. Android-Gallery[使用C# And Java实现]
  2. Unity3D 摄像机的Transform通过摇杆输出的方向
  3. thinkphp伪静态(url重写)
  4. css3 resize box-sizing outline-offset
  5. poj1179
  6. 九度oj 1521 二叉树的镜像
  7. 编写类String的构造函数、拷贝构造函数、析构函数和赋值函数
  8. 使sublimetext3在ubuntu下可以打中文和在windows的dos命令行下正常显示中文
  9. Linux命令pmap
  10. VirtualBox不能为虚拟电脑打开一个新任务——The VirtualBox kernel modules do not match this version of VirtualBox
  11. result type
  12. netty入门demo(一)
  13. The 2018 ACM-ICPC Asia Beijing Regional Contest
  14. 百度地图的Icon
  15. Java多线程-----理解CountDownLatch
  16. 使用AndroidStudio导入github项目
  17. tkinter如何设置界面消失 当制作一个登陆界面时,登陆成功之后,如何让登陆界面本身消失
  18. SQL-Server多表关联查询并分页
  19. 一篇关于介绍php的几个user 认证相关的几个包
  20. Wireshark、Netcat

热门文章

  1. 网络编程OSI介绍
  2. 直播问答App乃虚火,调侃知识终不能长久盈利
  3. java的23种设计模式之建造者模式
  4. 推荐一款在UBUNTU下使用的编辑器
  5. 使用hexo,创建博客
  6. 乱世兄弟(豹老头 X 天捣臼)
  7. 最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/
  8. HTTP协议 有这篇文章足够了
  9. Samtec 5G探索之路
  10. VUE实现Studio管理后台(二):Slot实现选项卡tab切换效果,可自由填装内容