python 全栈开发,Day75(Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件)
昨日内容回顾
基于对象的跨表查询 正向查询:关联属性在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/
直接点击注册,相关的输入框就会有提示。这些就是利用局部刷新做到的!
输入框绑定了blur事件(当输入完用户名以后触发动作)
优点:
- AJAX使用Javascript技术向服务器发送异步请求
- AJAX无须刷新整个页面
简单来说,1.异步请求。2.局部刷新
Ajax流程图
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,就会出现弹框
简单的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按钮,底部出现一本书
那么,它经历了怎样的过程呢?请参考上面的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/
效果如下:
模拟停顿
先点击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秒,出现书籍
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
查看响应页面,这个页面看着熟悉吧。被django的csrf模块拦截了!
那么如何解决这个问题呢?
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代码
发现有一个input标签,name名为csrfmiddlewaretoken。后面有一个value值,这个是django生成的。每次刷新页面,它会变动!
我们不可能像爬虫一样,把这个value给爬下来!终极办法就是通过dom来获取input的值
通过属性选择器,可以精确的查找出input的值
$("[name=csrfmiddlewaretoken]")[0]
使用console来模拟dom操作
获取value值,使用val()
注意:在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>
刷新页面,再次计算,就可以正常使用了!
基于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/
效果如下:
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: { '403': function (jqXHR, textStatus, err) { console.log(arguments); }, '400': 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/
填写信息,选择一个图片,点击提交
查看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
访问页面,重新上传图片
查看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左侧会多出一张图片,默认是保存在项目根目录的
指定路径存储
创建目录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
再次上传图片
就会保存在指定路径了
二、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', ]
访问首页
点击按钮
打开谷歌浏览器工具-->network
点击这个请求
查看Headers,查看Form Date,点击view sorce,查看原始数据
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,查看原始数据
发现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">控件
访问网页,选择一个文件
点击提交,提示上传成功
四、SweetAlert插件
sweetalert是一个漂亮的弹窗
中文网址:
http://mishengqiang.com/sweetalert/
它需要2个文件:sweetalert-dev.js和sweetalert.css
下载插件
怎么下载呢?直接从上面的网站扣下来,就可以了
修改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,效果如下:
表格删除一条记录
修改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="1" 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="2" 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="3" 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是一个自定义属性,属性名,可以随便。
删除一条记录,效果如下:
注意:页面并没有刷新。那么第2条记录,是如何没有的呢?是用DOM操作,删除掉的!