python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

时间:2022-01-10 21:01:10

昨日内容回顾

基于对象的跨表查询

    正向查询:关联属性在A表中,所以A对象找关联B表数据,正向查询
反向查询:关联属性在A表中,所以B对象找A对象,反向查询 一对多: 按字段:xx
book ------------------ > publish
<--------------------
按表名小写__字段名。比如publish__name 多对多: 正 按字段:xx
book ------------------------- > author
<-------------------------
反 按表名小写__字段名 一对一 正 按字段:.ad
author ------------------------- > authordetail
<-------------------------
反 按表名小写 authordetail_obj.author

一、Django与Ajax

AJAX准备知识:JSON

什么是 JSON ?

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言 *
  • JSON 具有自我描述性,更易理解

* JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。

合格的json对象:

["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ]

不合格的json对象:

{ name: "张三", 'age': 32 }  // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined } // 不能使用undefined
{ "name": "张三",
"birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
"getName": function() {return this.name;} // 不能使用函数和日期对象
}

json支持7种数据格式

python 原始类型向 json 类型的转化对照表:

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

stringify与parse方法

JavaScript中关于JSON对象和字符串转换的两个方法:
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象

JSON.parse('{"name":"Q1mi"}');
JSON.parse('{name:"Q1mi"}') ; // 错误
JSON.parse('[18,undefined]') ; // 错误

JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。

JSON.stringify({"name":"Q1mi"})

和XML的比较

JSON 格式于2001年由 Douglas Crockford 提出,目的就是取代繁琐笨重的 XML 格式。

JSON 格式有两个显著的优点:书写简单,一目了然;符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON迅速被接受,已经成为各大网站交换数据的标准格式,并被写入ECMAScript 5,成为标准的一部分。

XML和JSON都使用结构化方法来标记数据,下面来做一个简单的比较。

用XML表示中国部分省市数据如下:

<?xml version="1.0" encoding="utf-8"?>
<country>
<name>中国</name>
<province>
<name>黑龙江</name>
<cities>
<city>哈尔滨</city>
<city>大庆</city>
</cities>
</province>
<province>
<name>广东</name>
<cities>
<city>广州</city>
<city>深圳</city>
<city>珠海</city>
</cities>
</province>
<province>
<name>*</name>
<cities>
<city>台北</city>
<city>*</city>
</cities>
</province>
<province>
<name>*</name>
<cities>
<city>乌鲁木齐</city>
</cities>
</province>
</country>

用JSON表示如下:

{
"name": "中国",
"province": [{
"name": "黑龙江",
"cities": {
"city": ["哈尔滨", "大庆"]
}
}, {
"name": "广东",
"cities": {
"city": ["广州", "深圳", "珠海"]
}
}, {
"name": "*",
"cities": {
"city": ["台北", "*"]
}
}, {
"name": "*",
"cities": {
"city": ["乌鲁木齐"]
}
}]
}

由上面的两端代码可以看出,JSON 简单的语法格式和清晰的层次结构明显要比 XML 容易阅读,并且在数据交换方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽。

Ajax简介

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

  • 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
  • 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。

AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

应用情景

搜索引擎根据用户输入的关键字,自动提示检索关键字。

还有一个很重要的应用场景就是注册时候的用户名的查重。

其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。

  • 整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
  • 当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!

比如博客园的注册页面:

https://account.cnblogs.com/

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

直接点击注册,相关的输入框就会有提示。这些就是利用局部刷新做到的!

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

输入框绑定了blur事件(当输入完用户名以后触发动作)

优点:

  • AJAX使用Javascript技术向服务器发送异步请求
  • AJAX无须刷新整个页面

简单来说,1.异步请求。2.局部刷新

Ajax流程图

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

1、客户端触发异步操作
2、创建新的XMLHttpRequest对象,这是ajax的核心(需要着重的学习下)
3、通过send()方法实现与server的连接4
4、服务器端接收请求,并处理
5、返回处理的结果,这个结果可以是XML文档、也可以是josn字符串(一般情况下josn就可以处理大部分的结果、而且相对的比较好操作)
6、在客户端去接收服务器传回来的结果,并且通过javascript进行你想要的处理

发请求给服务器的途径:

1. 地址栏:get
2. form表单,支持get和post
3. 超链接 <a href="/path/">click</a> 这种是get方式
4. Ajax请求: 可以指定get和post

发Ajax请求一般返回httpResponse()

案例

鼠标点击事件

效果:当点击click时,弹出提示框

准备工作:

使用Pycharm新建项目ajaxDemo

修改urls.py,增加路径index

from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]

修改views.py,增加index视图函数

def index(request):
return render(request,"index.html")

在创建index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script scr="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<button id="btn">click</button>
<script>
$("#btn").click(function () {
alert(123)
})
</script>
</body>
</html>

启动django项目,访问url:http://127.0.0.1:8000/index/

点击click,就会出现弹框

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

简单的ajax请求

效果:当点击click时,按钮底部出现一本书名

修改urls.py,增加books路径

urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
path('books/', views.books),
]

修改books视图函数

def books(request):
return HttpResponse("群山淡景")

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<button id="btn">click</button>
<p class="con"></p>
<script>
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"/books/", //请求的url
type:"get", //默认get
success:function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$(".con").text(data); //修改p标签的text值
}
})
})
</script>
</body>
</html>

访问url:点击click按钮,底部出现一本书

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

那么,它经历了怎样的过程呢?请参考上面的ajax流程图!

success表示请求成功,并拿到响应体之后,执行的动作!data是用来接收响应体的数据。data这个命令可以随便定义,约定成俗,使用data!

它接收HttpResponse,比如:《群山淡景》

最后是dom操作,修改HTML代码,实现了局部刷新!

ajax加法运算(get请求)

页面输入两个整数,通过AJAX传输到后端计算出结果并返回。

修改urls.py,增加cal路径

urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
path('books/', views.books),
path('cal/', views.cal),
]

修改views.py,增加cal视图函数

def cal(request):
a = request.GET.get("a") #获取第一个值,类型为字符串
b = request.GET.get("b") #获取第二个值
res = int(a) + int(b) # 必须要转换为数字才能计算
return HttpResponse(str(res)) # HttpResponse只能接收字符串

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body> <input type="text" id="n1"> + <input type="text" id="n2"> =
<input type="text" id="result">
<button id="cal">计算</button>
<script>
$("#cal").click(function () {
var n1 = $("#n1").val();
var n2 = $("#n2").val();
//发送ajax请求
$.ajax({
url: "/cal/", //请求的url
type: "get", //默认get
data: {
a: n1,
b: n2
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$("#result").val(data); //修改p标签的text值
}
})
})
</script>
</body>
</html>

访问url:http://127.0.0.1:8000/index/

效果如下:

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

模拟停顿

先点击click,在输出数字进行计算。等待5秒后,出现书籍!

编辑views.py,导入time模块,修改books视图函数

from django.shortcuts import render,HttpResponse
import time # Create your views here.
def index(request):
return render(request,"index.html") def books(request):
time.sleep(5)
return HttpResponse("群山淡景") def cal(request):
a = request.GET.get("a") #获取第一个值,类型为字符串
b = request.GET.get("b") #获取第二个值
res = int(a) + int(b) # 必须要转换为数字才能计算
return HttpResponse(str(res)) # HttpResponse只能接收字符串

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body>
<button id="btn">click</button>
<p class="con"></p>
<hr>
<input type="text" id="n1"> + <input type="text" id="n2"> =
<input type="text" id="result">
<button id="cal">计算</button>
<script>
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"/books/", //请求的url
type:"get", //默认get
success:function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$(".con").text(data); //修改p标签的text值
}
})
}); $("#cal").click(function () {
var n1 = $("#n1").val();
var n2 = $("#n2").val();
//发送ajax请求
$.ajax({
url: "/cal/", //请求的url
type: "get", //默认get
data: {
a: n1,
b: n2
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$("#result").val(data); //修改p标签的text值
}
})
});
</script>
</body>
</html>

刷新页面,先点击click,在输入数值,最后点击计算。

效果如下:等待5秒,出现书籍

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

ajax加法运算(post请求)

更改cal视图函数,改为post接收数据

def cal(request):
a = request.POST.get("a") #获取第一个值,类型为字符串
b = request.POST.get("b") #获取第二个值
res = int(a) + int(b) # 必须要转换为数字才能计算
return HttpResponse(str(res)) # HttpResponse只能接收字符串

更改index.html,ajax改为post请求

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body>
<input type="text" id="n1"> + <input type="text" id="n2"> =
<input type="text" id="result">
<button id="cal">计算</button>
<script>
$("#cal").click(function () {
var n1 = $("#n1").val();
var n2 = $("#n2").val();
//发送ajax请求
$.ajax({
url: "/cal/", //请求的url
type: "post", //默认get
data: {
a: n1,
b: n2
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$("#result").val(data); //修改p标签的text值
}
})
});
</script>
</body>
</html>

刷新页面,重新计算。发现没有反应,打开浏览器控制台-->network

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

查看响应页面,这个页面看着熟悉吧。被django的csrf模块拦截了!

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

那么如何解决这个问题呢?

1. 直接修改settings.py,注释掉csrf模块

2. post提交时,带上键值为csrfmiddlewaretoken的数据

第一种方案,显然不是我们想要的。我们选择第二种方案!

修改index.html,增加

{% csrf_token %}

完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body>
<input type="text" id="n1"> + <input type="text" id="n2"> =
<input type="text" id="result">
<button id="cal">计算</button>
{% csrf_token %}
<script>
$("#cal").click(function () {
var n1 = $("#n1").val();
var n2 = $("#n2").val();
//发送ajax请求
$.ajax({
url: "/cal/", //请求的url
type: "post", //默认get
data: {
a: n1,
b: n2
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$("#result").val(data); //修改p标签的text值
}
})
});
</script>
</body>
</html>

刷新页面,使用浏览器控制台,查看html代码

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

发现有一个input标签,name名为csrfmiddlewaretoken。后面有一个value值,这个是django生成的。每次刷新页面,它会变动!

我们不可能像爬虫一样,把这个value给爬下来!终极办法就是通过dom来获取input的值

通过属性选择器,可以精确的查找出input的值

$("[name=csrfmiddlewaretoken]")[0]

使用console来模拟dom操作

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

获取value值,使用val()

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

注意:在html标签里面, 只有input,select,textarea 这3个标签是用val拿值

修改index.html,增加参数csrfmiddlewaretoken

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body>
<input type="text" id="n1"> + <input type="text" id="n2"> =
<input type="text" id="result">
<button id="cal">计算</button>
{% csrf_token %}
<script>
$("#cal").click(function () {
var n1 = $("#n1").val();
var n2 = $("#n2").val();
var csrf = $("[name=csrfmiddlewaretoken]").val();
//发送ajax请求
$.ajax({
url: "/cal/", //请求的url
type: "post", //默认get
data: {
a: n1,
b: n2,
csrfmiddlewaretoken:csrf,
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体数据
$("#result").val(data); //修改p标签的text值
}
})
});
</script>
</body>
</html>

刷新页面,再次计算,就可以正常使用了!

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

基于Ajax进行登录验证

一般情况下,通常会将表单之类的标签放到form标签里面。这里我们使用form,只是把它当成一个容器而已!不使用submit按钮提交,而是使用ajax提交!使用div容器也是可以的!

注意点:

1. 不写action,默认用当前的url

2.botton标签放到form标签之后,它具有sumbit功能!它和submit效果是一样的,会刷新页面!

那么需要使用按钮怎么办?在input里面,有一个type="button"的。它有按钮效果,点击之后,没有任何反应!它在form表单里面,是安全的!没有默认事件!

那么它和ajax结合,就能实现某些功能。比如发送ajax请求!

修改index.html

注意:只要页面里面有下面的代码就可以,无论放到哪个位置都可以!只要jquery能获取到就行!

每次post提交,必须发送key为csrfmiddlewaretoken的值,否则提示403

这个是django给你发的身份证,如果没有身份证,那么django就会拦截

{% csrf_token %}

准备工作:准备一张表user

修改models.py,增加user表模型

from django.db import models

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

使用下面2个命令生成表

python manage.py makemigrations
python manage.py migrate

插入2条数据,注意修改表名

INSERT INTO app01_user (id, user, pwd) VALUES (1, 'xiao', 123);
INSERT INTO app01_user (id, user, pwd) VALUES (2, 'zhang', 123);

修改urls.py,增加login路径

urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
path('books/', views.books),
path('cal/', views.cal),
path('login/', views.login),
]

HttpResponse必须是一个字符串,因此想要返回一个字典,必须使用json序列化才行

修改views.py,增加login,完整代码如下:

from django.shortcuts import render,HttpResponse
from app01.models import User
import time
import json # Create your views here.
def index(request):
return render(request,"index.html") def books(request):
time.sleep(5)
return HttpResponse("群山淡景") def cal(request):
a = request.POST.get("a") #获取第一个值,类型为字符串
b = request.POST.get("b") #获取第二个值
res = int(a) + int(b) # 必须要转换为数字才能计算
return HttpResponse(str(res)) # HttpResponse只能接收字符串 def login(request):
user = request.POST.get("user")
pwd = request.POST.get("pwd")
#根据表单的用户名和密码到数据库中匹配
user_obj = User.objects.filter(user=user, pwd=pwd).first()
#一般请求下,需要定义一个字典。msg是约定成俗的名字,用来做提示的
response = {"user":None,"msg":None}
if user_obj: # 判断有返回结果的请求下
response["user"] = user_obj.user # 修改字典的用户名
else:
response["msg"] = "用户名或者密码不一致" # 修改提示信息
#返回json格式数据,默认序列化时,对中文默认使用的ascii编码。
# ensure_ascii=False表示显示真正的中文
return HttpResponse(json.dumps(response, ensure_ascii=False))

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body>
{% csrf_token %}
<h4>登录验证</h4>
<form>
<lable>用户名</lable><input type="text" id="user">
<lable>密码</lable><input type="password" id="pwd">
<input type="button" value="提交" id="login_btn">
{#显示错误信息#}
<span class="error"></span>
</form>
{% csrf_token %}
<script>
$("#login_btn").click(function () {
var csrf = $("[name=csrfmiddlewaretoken]").val();
//发送ajax请求
$.ajax({
url: "/login/", //请求的url
type: "post", //默认get
data: {
user: $("#user").val(),
pwd: $("#pwd").val(),
csrfmiddlewaretoken:csrf,
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体
console.log(typeof data); //打印数据类型
var data=JSON.parse(data); //反序列化数据 if(data.user){ // 登陆成功
//window.location.href表示跳转页面
alert("登录成功");window.location.href="/index/";
}
else{ // 登陆失败
//修改span标签,显示失败的返回值,并显示红色,左间距20px
$(".error").text(data.msg).css({"color":"red","margin-left":"20px"})
//设置定时器,2秒后清空提示信息
setTimeout(function () {
$(".error").text("") //清空提示信息
},2000)
}
}
})
});
</script>
</body>
</html>

注意:ajax里面的success接收的data响应体,必须要JSON.parse反序列才行

访问页面:http://127.0.0.1:8000/index/

效果如下:

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

ajax还有其他参数,可以设置,如下:

<button class="send_Ajax">send_Ajax</button>
<script> $(".send_Ajax").click(function(){ $.ajax({
url:"/handle_Ajax/",
type:"POST",
data:{username:"Yuan",password:123},
success:function(data){
console.log(data)
},
      
error: function (jqXHR, textStatus, err) {
console.log(arguments);
}, complete: function (jqXHR, textStatus) {
console.log(textStatus);
}, statusCode: {
'': function (jqXHR, textStatus, err) {
console.log(arguments);
}, '': function (jqXHR, textStatus, err) {
console.log(arguments);
}
} }) }) </script>

响应错误时,会执行error中的代码。

当 AJAX 请求正在进行时,执行complete的代码。

它可以做一个请求等待的效果!

二、文件上传

请求头ContentType

ContentType指的是请求体的编码类型,常见的类型共有3种:

1. application/x-www-form-urlencoded

这应该是最常见的 POST 提交数据的方式了。浏览器的原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了):

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8 user=yuan&age=22

2. multipart/form-data

这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 enctype 等于 multipart/form-data。直接来看一个请求示例:

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA ------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="user" yuan
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。

这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。

上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype 属性指定,默认为 application/x-www-form-urlencoded。其实 enctype 还支持 text/plain,不过用得非常少)。

随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。

3. application/json

application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。

JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。记得我几年前做一个项目时,需要提交的数据层次非常深,我就是把数据 JSON 序列化之后来提交的。不过当时我是把 JSON 字符串作为 val,仍然放在键值对里,以 x-www-form-urlencoded 方式提交。

基于form表单的文件上传

修改urls.py,增加路径file_put

urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
path('books/', views.books),
path('cal/', views.cal),
path('login/', views.login),
path('file_put/', views.file_put),
]

修改views.py,增加视图函数file_put

def file_put(request):
if request.method == "POST":
print(request.POST) # 打印POST信息 return HttpResponse("ok") return render(request, "file_put.html") # 渲染页面file_put.html

在templates里面增加页面file_put.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <h3>form表单文件上传</h3>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="text" name="user">
<input type="file" name="img"><br/><br/>
<input type="submit">
</form>
</body>
</html>

注意:enctype的类型不同,发送数据格式也会不同。

表单默认为application/x-www-form-urlencoded。它的数据格式为key1=value1&key1=value1形式。

它不能发送图片,那么需要指定为multipart/form-data才可以!

访问url:http://127.0.0.1:8000/file_put/

填写信息,选择一个图片,点击提交

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

查看Pycharm控制台输出信息:

<QueryDict: {'user': ['xiao'], 'csrfmiddlewaretoken': ['SM7KpTsvoeIDNycBiHbrF3AtEqcLTwJh6uiDzyccL3gVQHgbmfTokD3zmb3hExoK']}>

从属性信息中,可以看出,没有img的数据。那么它跑到哪里去了呢?

因为django对于文件,单独做了一个属性request.FILES

获取上传图片

修改file_put视图函数

def file_put(request):
if request.method == "POST":
print(request.POST) # 打印POST信息
print(request.FILES) # 打印文件信息
return HttpResponse("ok") return render(request, "file_put.html") # 渲染页面file_put.html

再次访问页面,重新提交数据,再次查看Pycharm控制台输出信息:

<QueryDict: {'user': ['xiao'], 'csrfmiddlewaretoken': ['SM7KpTsvoeIDNycBiHbrF3AtEqcLTwJh6uiDzyccL3gVQHgbmfTokD3zmb3hExoK']}>
<MultiValueDict: {'img': [<InMemoryUploadedFile: 161022vkhyigaq4si947qv.jpg (image/jpeg)>]}>

这次得到了img信息,它的类型为MultiValueDict。描述了图片的文件名以及图片类型jpeg

下载图片

修改file_put视图函数

def file_put(request):
if request.method == "POST":
print(request.POST) # 打印POST信息
print(request.FILES) # 打印文件信息 file_obj = request.FILES.get("img") # 获取img
print('type',type(file_obj))
print(file_obj.__dict__) # 打印img对象属性
print(file_obj.name) # 打印文件名
with open(file_obj.name,"wb") as f: # 打开文件
for line in file_obj:
f.write(line) # 写入文件 return render(request, "file_put.html") # 渲染页面file_put.html

访问页面,重新上传图片

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

查看Pycharm控制台输出

<QueryDict: {'csrfmiddlewaretoken': ['rxcgytunhFZSMFsHH6FDfqzNCy5uurUQFfn9I8e4EuxaPOwhLEnAU02TkjW0fszj'], 'user': ['xiao']}>
<MultiValueDict: {'img': [<InMemoryUploadedFile: 2011112919211178.gif (image/gif)>]}>
type <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
{'_name': '2011112919211178.gif', 'content_type': 'image/gif', 'file': <_io.BytesIO object at 0x000001D616EA4F10>, '_size': 14444, 'charset': None, 'field_name': 'img', 'content_type_extra': {}}
2011112919211178.gif

Pycharm左侧会多出一张图片,默认是保存在项目根目录的

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

指定路径存储

创建目录static,在static里面创建images

修改file_put视图函数

def file_put(request):
if request.method == "POST":
print(request.POST) # 打印POST信息
print(request.FILES) # 打印文件信息 file_obj = request.FILES.get("img") # 获取img
print('type',type(file_obj))
print(file_obj.__dict__) # 打印img对象属性
print(file_obj.name) # 打印文件名
print()
with open("static/images/"+file_obj.name,"wb") as f: # 打开文件
for line in file_obj:
f.write(line) # 写入文件 return render(request, "file_put.html") # 渲染页面file_put.html

再次上传图片

就会保存在指定路径了

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

二、ajax传输json数据

修改urls.py,添加路径ajax_handle

urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
path('books/', views.books),
path('cal/', views.cal),
path('login/', views.login),
path('file_put/', views.file_put),
path('ajax_handle/', views.ajax_handle),
]

修改views.py,增加ajax_handle视图函数

def ajax_handle(request):
print(request.POST)
return HttpResponse('ok')

修改index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body> <button class="btn2">click</button>
<script>
$(".btn2").click(function () {
//发送ajax请求
$.ajax({
url: "/ajax_handle/", //请求的url
type: "post", //默认get
data: {
a: 1,
b: 2,
},
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体
}
})
});
</script>
</body>
</html>

修改settings.py,先关闭掉csrf

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

访问首页

点击按钮

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

打开谷歌浏览器工具-->network

点击这个请求

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

查看Headers,查看Form Date,点击view sorce,查看原始数据

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

 ajax和form默认都是application/x-www-form-urlencoded; 

urlencoded的数据格式是a=1&b=2这种格式

指定ContentType

请求头ContentType有3种类型,最常用的是第1,3这两种类型。

那么ajax如果要发送json,需要声明ContentType类型

修改index.html,简单写法:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<style type="text/css">
input {
width: 50px;
}
</style>
</head>
<body> <button class="btn2">click</button>
<script>
$(".btn2").click(function () {
//发送ajax请求
$.ajax({
url: "/ajax_handle/", //请求的url
type: "post", //默认get
contentType:"json", //声明为json
data: JSON.stringify({ //json序列化数据
a: 1,
b: 2,
}),
success: function (data) { //data接收响应体,必须要有
console.log(data); //打印响应体
}
})
});
</script>
</body>
</html>

声明json的完整写法为:

contentType:"application/json",

重新访问网页,点击按钮,发送数据

查看Headers,查看Form Date,点击view sorce,查看原始数据

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

发现ContentType变动了,那么数据也会相应变动

注意:form表单不能发送json数据,只能由ajax发送!

查看Pycharm控制台输出:

<QueryDict: {}>

为什么django视图函数,接收的POST数据是空的呢?明明发过来了啊!

因为wsgi接收数据时,它会对ContentType做if判断。当ContentType为application/x-www-form-urlencoded时,并且请求方式为POST时,将数据给request.POST封装成一个字典!

那么application/json的数据,在哪里呢?在request.body里面!

body是请求体的内容,它接收完整的请求体数据!它是原始数据

修改ajax_handle视图函数

def ajax_handle(request):
print(request.POST)
print(request.body)
return HttpResponse('ok')

再次访问网页,点击按钮,发送数据

查看Pycharm控制台输出:

<QueryDict: {}>
b'{"a":1,"b":2}'

注意:request.body,它是原始数据,并没有做任何封装。它还是一个bytes类型,django没有提供接口来解析application/json数据

request.POST是django提供了方法,进行解析数据,返回有一个字典,很容易取到数据!

解析request.body数据

由于它是json数据,那么使用json模块,进行反序列化,就可以了!

修改ajax_handle视图函数

def ajax_handle(request):
print("body",request.body)
print("POST",request.POST)
#由于是一个bytes类型,需要解码。再用json反序列化才行
data = json.loads(request.body.decode("utf-8"))
print(data) #打印json
print(data["a"]) # 取key为a的值
return HttpResponse('ok')

查看Pycharm控制台输出:

body b'{"a":1,"b":2}'
POST <QueryDict: {}>
{'b': 2, 'a': 1}
1

从输出信息上来看,经过反序列化之后,也可以方便的取值。

三、基于Ajax的文件上传

利用ajax和FormData实现页面无刷新的文件上传效果,主要用到了jQuery的ajax()方法和XMLHttpRequest Level 2的FormData接口。关于FormData,大家可以看MDN文档

修改file_put视图函数

def file_put(request):
if request.method == "POST":
print(request.POST) # 打印POST信息
print(request.FILES) # 打印文件信息 file_obj = request.FILES.get("img") # 获取img
print('type',type(file_obj))
print(file_obj.__dict__) # 打印img对象属性
print(file_obj.name) # 打印文件名 response = {"state":False}
with open("static/images/"+file_obj.name,"wb") as f: # 打开文件
for line in file_obj:
ret = f.write(line) # 写入文件
print(ret) # 返回的是写入的字符长度
if ret: # 判断返回值
response["state"] = True return HttpResponse(json.dumps(response)) # 返回json return render(request, "file_put.html") # 渲染页面file_put.html

修改file_put.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% csrf_token %}
<h3>form表单文件上传</h3>
<form>
用户名 <input type="text" id="user"><br/>
头像 <input type="file" id="avatar"><br/><br/>
<input type="button" id="ajax-submit" value="ajax-submit">
</form> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script> $("#ajax-submit").click(function(){
var csrf = $("[name=csrfmiddlewaretoken]").val(); //csrf
var formdata=new FormData(); //实例化了一个空的FormData对象
formdata.append("csrfmiddlewaretoken",csrf); //给当前FormData对象添加一个键/值对.
formdata.append("user",$("#user").val());
formdata.append("img",$("#avatar")[0].files[0]);
$.ajax({ url:"", //表示为当前url
type:"post",
data:formdata, //发送一个FormData对象
processData: false , // 不处理数据
contentType: false, // 不设置内容类型 success:function(data){
var data = JSON.parse(data); //反序列化数据
console.log(data);
if (data.state){ //判断返回值
//弹出提示框,并刷新整个页面
alert('上传成功');window.location.href="/file_put/";
}else {
alert('上传失败');
} }
}) }) </script>
</body>
</html>

代码很简单,需要注意的是页面中没有用到form表单,那么怎么提交数据呢,答案是用FormData来模拟表单中的<input type="file" id="avatar">控件

访问网页,选择一个文件

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

点击提交,提示上传成功

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

四、SweetAlert插件

sweetalert是一个漂亮的弹窗

中文网址:

http://mishengqiang.com/sweetalert/

它需要2个文件:sweetalert-dev.js和sweetalert.css

下载插件

怎么下载呢?直接从上面的网站扣下来,就可以了

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

修改index.html,引入2个资源

<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>
<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">

首页有一个删除还不错,直接贴过来即可!

index.html完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>
<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">
</head>
<body> <button class="btn2">click</button>
<script>
$(".btn2").click(function () {
//删除示例代码
swal({
title: "确定删除吗?",
text: "你将无法恢复该虚拟文件!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "确定删除!",
cancelButtonText: "取消删除!",
closeOnConfirm: false,
closeOnCancel: false
},
function (isConfirm) {
if (isConfirm) {
swal("删除!", "你的虚拟文件已经被删除。",
"success");
} else {
swal("取消!", "你的虚拟文件是安全的:)",
"error");
}
}); });
</script>
</body>
</html>

访问首页,点击click,效果如下:

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

表格删除一条记录

修改settings.py,开启csrf

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

修改views.py,修改ajax_handle视图函数,返回一个json

def ajax_handle(request):
if request.method == "POST":
print(request.POST)
#定义一个状态,假装删除成功了
response = {"state": True}
return HttpResponse(json.dumps(response)) # 返回json
else:
return HttpResponse("非法请求,必须是POST")

修改index.html,写一个table,模拟删除操作

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>
<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">
</head>
<body>
{% csrf_token %}
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h3>数据展示</h3>
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
<th>operation</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
<td><input type="button" class="btn btn-danger" data-toggle="modal" line_num="" value="delete"/>
</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
<td><input type="button" class="btn btn-danger" data-toggle="modal" line_num="" value="delete"/>
</td>
</tr>
<tr>
<th scope="row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>@twitter</td>
<td><input type="button" class="btn btn-danger" data-toggle="modal" line_num="" value="delete"/>
</td>
</tr> </tbody>
</table>
</div>
</div>
</div>
<script>
$(".btn.btn-danger").click(function () {
var line_num = $(this).attr("line_num"); //一行数据的id值
var _this = $(this); //选择删除的那一行
var csrf = $("[name=csrfmiddlewaretoken]").val(); //获取csrf input的value值 swal({
title: "亲,您确定删除吗?",
text: "删除可就找不回来了哦!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "确定删除!",
cancelButtonText: "取消删除!",
closeOnConfirm: false,
closeOnCancel: false
},
function (isConfirm) {
if (isConfirm) {
$.ajax({
url: "/ajax_handle/",
type: "post",
data: {
'id': line_num,
csrfmiddlewaretoken: csrf,
},
success: function (data) {
var data = JSON.parse(data); //反序列化数据
if (data.state) { //判断json的状态
swal("删除成功!", "记录已经被删除。",
"success");
_this.parent().parent().remove(); //移除tr标签
} else {
swal("删除失败!", "删除失败,请重试!)",
"error");
window.location = "/index/"; //跳转首页
}
} });
} else {
swal("取消!", "你的数据是安全的:)",
"error");
}
}); });
</script>
</body>
</html>

注意:在input里面定义的line_num是一个自定义属性,属性名,可以随便。

删除一条记录,效果如下:

python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)

注意:页面并没有刷新。那么第2条记录,是如何没有的呢?是用DOM操作,删除掉的!