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
View Code

 

一、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>
View Code

用JSON表示如下:

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

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

 

Ajax简介

AJAXAsynchronous Javascript And XML)翻译成中文就是异步JavascriptXML”。即使用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),
]
View Code

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

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

在创建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>
View Code

启动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),
]
View Code

修改books视图函数

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

修改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>
View Code

 访问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),
]
View Code

修改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只能接收字符串
View Code

修改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>
View Code

访问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只能接收字符串
View Code

修改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>
View Code

刷新页面,先点击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只能接收字符串
View Code

更改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>
View Code

刷新页面,重新计算。发现没有反应,打开浏览器控制台-->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>
View Code

刷新页面,使用浏览器控制台,查看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>
View Code

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

 

 

基于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)
View Code

使用下面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);
View Code

修改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),
]
View Code

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))
View Code

修改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>
View Code

注意: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>
View Code

响应错误时,会执行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
View Code

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--
View Code

这个例子稍微复杂点。首先生成了一个 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),
]
View Code

修改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
View Code

在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>
View Code

注意: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']}>
View Code

从属性信息中,可以看出,没有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
View Code

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

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

这次得到了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
View Code

访问页面,重新上传图片

查看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
View Code

 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
View Code

再次上传图片

就会保存在指定路径了

 

二、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),
]
View Code

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

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

修改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>
View Code

修改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',
]
View Code

访问首页

点击按钮

打开谷歌浏览器工具-->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>
View Code

声明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')
View Code

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

查看Pycharm控制台输出:

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

注意: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')
View Code

查看Pycharm控制台输出:

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

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

 

三、基于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
View Code

修改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>
View Code

代码很简单,需要注意的是页面中没有用到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>
View Code

访问首页,点击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',
]
View Code

修改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")
View Code

修改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>
View Code

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

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

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

 

posted @ 2018-07-03 19:14  肖祥  阅读(830)  评论(3编辑  收藏  举报