昨日内容回顾

基于对象的跨表查询

    正向查询:关联属性在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: {
'': 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/

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

查看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="" 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是一个自定义属性,属性名,可以随便。

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

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

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

  1. Django与Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传,SweetAlert插件

    一.Django与Ajax AJAX准备知识:JSON 什么是 JSON ? JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻 ...

  2. Python全栈开发:django网络框架(二)

    Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行 ...

  3. Python全栈开发:django网络框架(一)

    Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. ...

  4. Python全栈开发记录_第八篇(模块收尾工作 json & pickle & shelve & xml)

    由于上一篇篇幅较大,留下的这一点内容就想在这里说一下,顺便有个小练习给大家一起玩玩,首先来学习json 和 pickle. 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不过, ...

  5. python 全栈开发,Day50(Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏)

    一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...

  6. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

  7. python全栈开发目录

    python全栈开发目录 Linux系列 python基础 前端~HTML~CSS~JavaScript~JQuery~Vue web框架们~Django~Flask~Tornado 数据库们~MyS ...

  8. Python全栈开发相关课程

    Python全栈开发 Python入门 Python安装 Pycharm安装.激活.使用 Python基础 Python语法 Python数据类型 Python进阶 面向对象 网络编程 并发编程 数据 ...

  9. Python 全栈开发【第0篇】:目录

    Python 全栈开发[第0篇]:目录   第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...

随机推荐

  1. scatter

    一.matplotlib.pyplot.scatter用来画散点图 import matplotlib.pyplot as plt import matplotlib as mpl mpl.rcPar ...

  2. 学习Git笔记

    一.名词解释 1.仓库(Repository) 仓库用来存放项目代码,每个项目对应一个仓库,多个开源项目则有多个仓库. 2.收藏(Star) 收藏项目,方便下次查看 3.复制克隆项目(Fork) 该f ...

  3. Linux重启服务器步骤

  4. webstorm 很卡 scanning files to index (扫描文件索引)

    webstorm 号称"前端神器",但npm导入包跑索引,会很卡不停的跑索引... 排除你不想索引的文件夹 找到你想排除的文件夹(主要是node_modulewe文件夹),右键选择 ...

  5. 参数在一个线程中各个函数之间互相传递的问题(ThreadLocal)

    ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源. 一个ThreadLocal变量虽然是 ...

  6. Android如何降低service被杀死概率

    http://www.jianshu.com/p/06a1a434e057 http://www.cnblogs.com/ylligang/articles/2665181.html Android应 ...

  7. Android APP常见的5类内存泄露及解决方法

    1.static变量引起的内存泄漏 因为static变量的生命周期是在类加载时开始 类卸载时结束,也就是说static变量是在程序进程死亡时才释放,如果在static变量中 引用了Activity 那 ...

  8. 移动前端框架,require.js压缩

    static css images 不同的页面可以新建不同的图片文件夹(可选) js libs       前端类库 plugs    插件 views    自己写的代码文件 sass/less l ...

  9. PLSQL_day01

    declare begin  dbms_output.put_line('Hello world') end;

  10. 【逆向工具】使用x64dbg+spy去除WinRAR5.40(64位)广告弹框

    1 学习目标 WinRAR5.40(64位)的弹框广告去除,由于我的系统为x64版本,所以安装了WinRAR(x64)版本. OD无法调试64位的程序,可以让我熟悉x64dbg进行调试的界面. 其次是 ...