Diango基础七之Ajax

Django基础七之Ajax

一 Ajax简介

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

  2.简单示例

html:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>AJAX局部刷新实例</title> </head> <body> <input type="text" id="i1">+ <input type="text" id="i2">= <input type="text" id="i3"> <input type="button" value="AJAX提交" id="b1"> <script src="/static/jquery-3.2.1.min.js"></script> <script> $("#b1").on("click", function () { $.ajax({ url:"/ajax_add/", //别忘了加双引号 type:"GET", data:{"i1":$("#i1").val(),"i2":$("#i2").val()}, //object类型,键值形式的,可以不给键加引号 success:function (data) { $("#i3").val(data); } }) }) </script> </body> </html>
views.py里面的内容:

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


def ajax_add(request):
#time.sleep(10) #不影响页面发送其他的请求 i1 = int(request.GET.get("i1")) i2 = int(request.GET.get("i2")) ret = i1 + i2 return JsonResponse(ret, safe=False)
#return render(request,'index.html') #基于AJax的特点,不会返回一个页面没有意义,就是一堆的字符串,拿到了这个页面,你怎么处理,你要做什么事情,根本就没有意义

 urls.py里面的内容

urlpatterns = [
    ...
    url(r'^ajax_add/', views.ajax_add),
    url(r'^ajax_demo1/', views.ajax_demo1),
    ...   
]

 3、应用场景:

  1、注册的时候用户名查询
  2、搜索引擎根据用户输入的关键字,自动提示检索关键字
  当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。整个过程中页面没有刷新,只是刷新页面中的局部位置而已!当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!

4、优缺点   

  1、AJAX使用JavaScript技术向服务器发送异步请求;
  2、AJAX请求无须刷新整个页面;
  3、因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;

5、 登陆案例:

html:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> 用户名:<input type="text" id="username"> 密码:<input type="text" id="pwd"> {% csrf_token %} <button id="sub">提交</button> <span style="color: red;font-size: 12px;" id="error"></span> </div> <script src="{% static 'jquery.js' %}"></script> <script> $('#sub').click(function () { $.ajax({ url:"{% url 'login' %}", type:'post', data:{username:$('#username').val(),pwd:$('#pwd').val(),csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val()}, success:function (data) { data = JSON.parse(data); console.log(data,typeof data); if (data['status']){ location.href=data['home_url']; #登陆成功跳转页面 } else { $('#error').text('用户名或者密码错误!') # 在span标签里 } } }) }) </script> </body> </html>


base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>
    欢迎来到xxx官网
</h1>
</body>
</html>

urls.py
url(r'^login/', views.login,name='login'),
url(r'^home/', views.home,name='home'),

views.py
def login(request):
    res_dict = {'status':None,'home_url':None}
    if request.method == 'GET':
        return render(request,'login.html')
    else:
        uname = request.POST.get('username')
        pwd = request.POST.get('pwd')

        user_obj = models.UserInfo.objects.filter(name=uname,password=pwd).exists()
        import json
        if user_obj:  #登陆成功
            res_dict['status'] = True
            res_dict['home_url'] = reverse('home')
            res_json_dict = json.dumps(res_dict)
return HttpResponse(res_json_dict)
       #直接回复字典格式是不可以的,必须转换成json字符串,转换成普通字符串也是不行的,因为前端需要对json进行反序列获得这个字典,在通过字典的形式来操作数据。 else: # 登陆失败 res_dict['status'] = False res_json_dict = json.dumps(res_dict) return HttpResponse(res_json_dict)
      # 如果你就是不使用JsonResponse的话,也可以给HttpResponse添加一个参数,content_type='application/json',
        那么前端ajax拿到数据之后,也是不需要反序列化的,ajax的回调函数就收到的就是一个反序列化之后的一个对象,因为ajax接受到数据后,
        通过这个data_type或者content_type发现你发送来的是个json格式的数据,那么ajax内容就自动将这个数据反序列化得到了js的数据对象,然后通过对象可以直接操作数据。
      # return HttpResponse(res_json_dict,data_type='application/json')
      # return JsonResponse(res_dict)
html:
def home(request): return render(request,'base.html')

 二 AJax的使用

1.基于jQuery的实现

<script>

       $(".send_Ajax").click(function(){

           $.ajax({
               url:"/handle_Ajax/",
               type:"POST",
               data:{username:"chao",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>
sucess/error/complete/statuscode 参数的理解:
查到资料补充:

  2.基于原生js实现

var b2 = document.getElementById("b2");
  b2.onclick = function () {
    // 原生JS
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("POST", "/ajax_test/", true);  # 文件路径
    xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
    xmlHttp.send("username=chao&password=123456");   # 传输内容
    xmlHttp.onreadystatechange = function () {
      if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
        alert(xmlHttp.responseText);  #服务端返回的内容
      }
    };
  };

3ajax参数

请求参数:
data:
data: 当前ajax请求要携带的数据,是一个json的object对象,ajax方法就会默认地把它编码成某种格式
     (urlencoded:?a=1&b=2)发送给服务端;此外,ajax默认以get方式发送请求。

     function testData() {
       $.ajax("/test",{     //此时的data是一个json形式的对象
          data:{
            a:1,
            b:2
          }
       });                   //?a=1&b=2
processData:
processData:
    声明当前的data数据是否进行转码或预处理,默认为true,即预处理;if为false,那么对data:{a:1,b:2}会调用json对象的toString()方法,即{a:1,b:2}.toString()最后得到一个[object,Object]形式的结果。
            
contentType

默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型用来指明当前请求的数据编码格式;urlencoded:?a=1&b=2;如果想以其他方式提交数据,比如contentType:"application/json",即向服务器发送一个json字符串:
$.ajax("/ajax_get",{

  data:JSON.stringify({
       a:22,
       b:33
   }),
   contentType:"application/json",
   type:"POST",

});                          //{a: 22, b: 33}

注意:contentType:"application/json"一旦设定,data必须是json字符串,不能是json对象

views.py:   json.loads(request.body.decode("utf8"))
django 中没有json数据类型的解码格式,需要手动解码。

traditional

一般是我们的data数据有数组时会用到 :data:{a:22,b:33,c:["x","y"]},traditional为false会对数据进行深层次迭代; 


响应参数: 
dataType:  
       预期服务器返回的数据类型,服务器端返回的数据会根据这个值解析后,传递给回调函数。 默认不需要显性指定这个属性,ajax会根据服务器返回的content Type来进行转换;比如我们的服务器响应的content Type为json格式,这时ajax方法就会对响应的内容 进行一个json格式的转换,if转换成功,我们在success的回调函数里就会得到一个json格式的对象;转换失败就会触发error这个回调函数。如果我们明确地指定目标类型,就可以使用data Type。 dataType的可用值:html|xml|json|text|script

 4  Ajax-服务器-Ajax流程图

三 Ajax请求设置csrf_token

方式1:通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。

$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "chao",
    "password": 123456,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})
方式2:
$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
方式3;
通过获取返回的cookie中的字符串 放置在请求头中发送。注意:需要引入一个jquery.cookie.js插件。
<script src="{% static 'js/jquery.cookie.js' %}"></script>  #  引入插件

$.ajax({
 
headers:{"X-CSRFToken":$.cookie('csrftoken')}, 
#其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加 
}) 
 
 
或者用自己写一个getCookie方法:
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

可以使用$.ajaxSetup()方法为ajax请求统一设置。
function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});

    注意:

      如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。

      如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。

      这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。

django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def login(request):
    pass

    更多细节详见:Djagno官方文档中关于CSRF的内容

四 Ajax文件上传

请求头ContentType

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

    1 application/x-www-form-urlencoded

    这应该是最常见的 POST 提交数据的方式了。浏览器的原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个。:

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

user=yuan&age=22   #这就是上面这种contenttype规定的数据格式,后端对应这个格式来解析获取数据,不管是get方法还是post方法,
都是这样拼接数据,大家公认的一种数据格式,但是如果你contenttype指定的是urlencoded类型,但是post请求体里面的数据是下面那种json的格式,那么就出错了,服务端没法解开数据。

   2 multipart/form-data

    一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 enctype 等于multipart/form-data,form表单不支持发json类型的contenttype格式的数据,而ajax什么格式都可以发,也是ajax应用广泛的一个原因。直接来看一。django不支持json数据类型的解码。
      这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
      上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype 属性指定,默认
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="user"

chao
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

  3 application/json 

  application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
  JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。
    注意事项:
        1、如果在ajax里面写上这个contenttype类型,那么data参数对应的数据,就不能是个object类型数据了,必须是json字符串,contenttype:'json',简写一个json,它也能识别是application/json类型,不推荐简写方式
      2、服务端接受到数据之后,通过contenttype类型的值来使用不同的方法解析数据,django里面不能帮我们解析contenttype值为json的数据格式,需要我们自己解析。
      3、contenttype:'json'   使用post或get获取数据时为空因为django不支持json数据类型的解析。
      4、使用json格式并且是pos请求提交数据时的注意事项:

 

  $.ajax({
            url:"{% url 'home' %}",
            type:'post',
            headers:{
                "X-CSRFToken":$.cookie('csrftoken'), #现在先记住,等学了cookies你就明白了
                contentType:'json',
            },

            data:JSON.stringify({ //如果我们发送的是json数据格式的数据,那么csrf_token就不能直接写在data里面了,没有效果,必须通过csrf的方式3的形式来写,写在hearders(请求头,可以写一些自定制的请求头)里面,注意,其实contentType也是headers里面的一部分,写在里面外面都可以
                name:name,
                //csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
            }),
            success:function (response) {

            }

        })

基于form表单的文件上传 

    模板部分


<form action="" method="post" enctype="multipart/form-data"> #上面说的其他两种contenttype都是键值的形式发送数据,这种form_data的格式一般是把大数据一段一段隔开的 用户名 <input type="text" name="user"> 头像 <input type="file" name="avatar"> #如果不用form_data格式来发,那么默认的是urlencoded的格式,这个标签的数据会组成avatar:文件名字来进行发送 <input type="submit"> </form>

    视图部分

def index(request):
    print(request.body)   # 原始的请求体数据
    print(request.GET)    # GET请求数据
    print(request.POST)   # POST请求数据
    print(request.FILES)  # 上传的文件数据

    return render(request,"index.html")

      upload.py

def upload(request):

    if request.method == 'GET':

        return render(request,'upload.html')
    else:
        print(request.POST)
        username = request.POST.get('user')
        file_obj = request.FILES.get('file_obj') #获得文件数据对象
        print('>>>',file_obj,type(file_obj))
        #>>> jaden博客.txt <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>,一个文件对象,可以理解为一个文件句柄
        file_name = file_obj.name #jaden博客.txt
        print(file_name)
        # 将数据写到文件里面,需要名字,需要数据
        with open(file_name,'wb') as f: #直接把文件名字放这里,那么文件将直接生成在django的整个项目目录下,因为django配置的系统搜索的根路径就是咱们的项目文件夹路径,那个BASE_DIR,一般我们需要自己建立一个文件夹专门存放上传的文件
      #所以需要我们自己来拼接一个路径放到这里,os.path.join(settings.BASE_DIR,'media','img',file_name)
# f.write() #不能一下写进去,占用的内容太多,要一点一点写 for data in file_obj: #读数据 f.write(data) #每次读取的data不是固定长度的,和读取其他文件一样,每次读一行,识别符为\r \n \r\n,遇到这几个符号就算是读了一行
       #for chunks in file_obj.chunks(): #chunks()默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器
     #  f.write(chunks)

为什么要将jquery对象装换为dom对象:

      

基于Ajax的文件上传

  模板

复制代码
<form> #用不用form没关系,这里就是个盒子的作用,一般写form标签是为了提示别人,这个地方的内容是要提交的
      {% csrf_token %}
用户名 <input type="text" id="user"> 头像 <input type="file" id="avatar"> <input type="button" id="ajax-submit" value="ajax-submit"> </form> <script> $("#ajax-submit").click(function(){ var formdata=new FormData(); #ajax上传文件的时候,需要这个类型,它会将添加给它的键值对加工成formdata的类型
     使用js 获取:
     # var form = document.getElementById("form1")
   # var fd = new FormData(form);
        formdata.append("user",$("#user").val());  #添加键值的方法是append,注意写法,键和值之间是逗号
     formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); #别忘了csrf_token
        formdata.append("avatar_img",$("#avatar")[0].files[0]);
        $.ajax({

            url:"",
            type:"post",
            data:formdata, #将添加好数据的formdata放到data这里
            processData: false ,    // 不处理数据
            contentType: false,    // 不设置内容类型

            success:function(data){
             console.log(data)
            }
        })

    })

</script>
复制代码

      这样也可以直接通过ajax 的 send() 方法将 fd 发送到后台。

      注意:由于 FormData 是 XMLHttpRequest Level 2 新增的接口,现在 低于IE10 的IE浏览器不支持 FormData。

  视图

复制代码
def index(request):

    if request.is_ajax():

        print(request.body)   # 原始的请求体数据
        print(request.GET)    # GET请求数据
        print(request.POST)   # POST请求数据
        print(request.FILES)  # 上传的文件数据

        return HttpResponse("ok")


    return render(request,"index.html")
复制代码

  检查浏览器的请求头:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaWl9k5ZMiTAzx3FT

 

    关于django后端代码接受上传文件的方法

当Django处理上传一个文件的时候,文件数据被放在request.FILES中。这个文档解释文件怎么样被存储在磁盘上或者内存中,怎样定制默认的行为。
基本文件上传
考虑一个包含FileField的简单的表单:
from  django  import  forms
classUploadFileForm(forms.Form):
   title=forms.CharField(max_length=50)
   file=forms.FileField()
一个处理这个表单的视图将在request.FILES中接受文件数据 ,request.FILES是一个字典,它对每个FileField(或者是ImageField,或者是其他的FileField的子类)都包含一个key.所以 从上面的表单中来的数据将可以通过request.FILES['file']键来访问.
注意request.FILES只有 在request方法是POST并且发出POST请求的

有属性enctype="multipart/form-data".否则,request。FILES将是空的。
看另一个简单的;
from fdjango.http improt HttpResponseRedirect
from django.shortcuts import render_to_response
from somewhere import handle_uploaded_file
def upload_file(request):
    if request.method == 'post':
        form =  UploadFileForm(rquest.POST,request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponseRedirect('/success/ur/')
   else:
        form = UploadFileForm()
    return render_to_response('upload.html',{'form':form})
要注意,我们必须将request.FILES传递到表单的构造器中;这就是文件数据怎样和表单沾上边的 。
处理上传的文件
最后的难题是怎样处理从request.FILES中获得的真实的文件。这个字典的每个输入都是一个UploadedFile对象——一个上传之后的文件的简单的包装。
你通常会使用下面的几个方法来访问被上传的内容:
UploadedFile.read():从文件中读取整个上传的数据。小心整个方法:如果这个文件很大,你把它读到内存中会弄慢你的系统。你可以想要使用chunks()来代替,看下面;
UploadedFile.multiple_chunks():如果上传的文件足够大需要分块就返回真。默认的这个值是2.5兆,当然这个值是可以调节的,看下面的UploadedFile.chunks():一个产生器,返回文件的块。如果multiple_chunks()是真的话,你应该在一个循环中使用这个方法,而不是使用read();
UploadedFile.name:上传文件的名字(比如m_file.txt)
UploadedFile.size:以bytes表示的上传的文件的大小。
还有其他的几个方法和属性。你可以自己去查。
把他们放在一起,这里是一个你处理上传文件的通常方法:
def handle_uploaded_file(f):
    destination = open('some/file/name.txt','wb+')
    for chunk in f.chunks(): 
        destination.write(chunk)
    destination.close()
在UploadedFile.chunks()上循环而不是用read()保证大文件不会大量使用你的系统内存。
上传的数据存在哪里?
在你保存上传的文件之前,数据需要被保存在某些地方。默认呢的,如果一个上传的文件小于2.5兆,Django会将上传的东西放在内存里。这意味着只要从内存读取数据并保存到硬盘上,所以很快。然而,如果一个上传的文件太大,Django将将上传的文件写到一个临时的文件中,这个文件在你的临时文件路径中。在Unix-like的平台上意味着你可以预见Django产生一个文件保存为/tmp/tmpzfp6I6.upload的文件。如果这个文件足够大,你可以观察到这个文件的大小在增大。
很多细节--2.5M;/tmp;等 等 都是简单的看上去合理的默认值。继续阅读看看你怎么样个性化或者完全替代掉上传行为。
改变上传处理行为
三个设置改变Django的上传处理行为:
FILE_UPLOAD_MAX_MEMORY_SIZE:以bytes为单位的到内存中的最大大小,。比这个值大的文件将被先存到磁盘上。默认是2.5兆
FILE_UPLOAD_TEMP_DIR:比FILE_UPLOAD_MAX_MEMORY_SIZE大的文件将被临时保存的地方。默认是系统标准的临时路径。
FILE_UPLOAD_PERMISSIONS:如果这个没有给出或者是None,你将获得独立于系统的行为。大多数平台,临时文件有一个0600模式,从内存保存的文件将使用系统标准umask。
FILE_UPLOAD_HANDLERS:上传文件的处理器。改变这个设置允许完全个性化——甚至代替——Django的上传过程。
默认是:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
 "django.core.files.uploadhandler.TemporaryFileUploadHandler",)
UploadedFile 对象
class UploadedFile
作为那些重File继承的补充,素有的UploadedFile对象定义了下面的方法和属性:
UploadedFile.content_type
文件的content_type头(比如text/plain
 orapplication/pdf
)。像用户提供的任何数据一样,你不应该信任上传的数据就是这个类型。你仍然要验证这个文件包含这个头声明的content-type——“信任但是验证”。
UploadedFile.charset
对于text/*的content-types,浏览器提供的字符集。再次,“信任但是验证”是最好的策略。
UploadedFile.temporary_file_path():只有被传到磁盘上的文件才有这个方法,它返回临时上传文件的全路径。
注意:
 像通常的Python文件,你可以迭代上传的文件来一行一行得读取文件:
for line in uploadedfile:
    do_something_with(line)
然而,不同于标准Python文件,UploadedFile值懂得/n(也被称为Unix风格)的结尾。如果你知道你需要处理有不同风格结尾的文件的时候,你要在你的视图中作出处理。
上传处理句柄:
当一个用户上传一个文件,Django敬爱那个这个文件数据传递给上传处理句柄——一个处理随着文件上传处理文件的小类。上传处理句柄被FILE_UPLOAD_HANDLERS初始化定义,默认是:
(
"django.core.files.uploadhandler.MemoryFileUploadHandler"
,
 "django.core.files.uploadhandler.TemporaryFileUploadHandler"
,)
这两个提供了Django处理小文件和大文件的默认上产行为。
你可以个性化处理句柄来个性化Django处理文件的行为。比如你可以使用个性化的处理句柄来强制用户配额,实时地压缩数据,渲染进度条,甚至在保存在本地的同时向另一个存储地发送数据。
实时修改上传处理句柄
有的时候某些视图要使用不同的上传行为。这种情况下,你可以重写一个上传处理句柄,通过request.upload_handlers来修改。默认的,这个列表包含FILE_UPLOAD_HANDLERS提供的处理句柄,但是你可以像修改其他列表一样修改这个列表。
比如,加入你写了一个叫做
ProgressBarUploadHandler
 的处理句柄。你可以通过下面的形式加到你的上传处理句柄中:
request.upload_handlers.insert(0,ProgressBarUploadHandler())
你赢使用list.insert()在这种情况下。因为进度条处理句柄需要首先执行。记住,处理句柄按照顺序执行。
如果你像完全代替掉上传处理句柄,你可以赋值一个新的列表:
request.upload_handlers=[ProgressBarUploadHandler()]
注意:你只能在访问request.POST或者request.FILES之前修改上传处理句柄。——如果上传处理开始后再改就没用了。如果你在修改reqeust.uplaod_handlers之前访问了request.POST
 or request.FILES
 ,Django将抛出一个错误。
所以,在你的视图中尽早的修改上传处理句柄。

写自定义的上传处理句柄:

所有的上传处理句柄都应 是 django.core.files.uploadhandler.FileUploadHandler的子类。你可以在任何你需要的地方定义句柄。
需要的方法:

自定义的上传处理句柄必须定义一下的方法:

FileUploadHandler.receive_data_chunk(self,raw_data,start):从文件上传中接收块。

raw_data是已经上传的字节流

start是raw_data块开始的位置

你返回的数据将被传递到下一个处理句柄的receive_data_chunk方法中。这样一个处理句柄就是另一个的过滤器了。

返回None将阻止后面的处理句柄获得这个块,当你 自己存储这个数据,而不想其他处理句柄存储拷贝时很有用。

如果你触发一个StopUpload或者SkipFile异常,上传将被放弃或者文件被完全跳过。

FileUploadHandler.file_complete(self, file_size)


当 文件上传完毕时调用。

处理句柄应该返回一个UploadFile对象,可以存储在request.FILES中。处理句柄也可以返回None来使得UploadFile对象应该来自后来的上传处理句柄。


剩下的就是可选的一些方法实现。


FILE_UPLOAD_MAX_MEMORY_SIZE = 209715200 
FILE_UPLOAD_MAX_MEMORY_SIZE = 209715200


在你本机先好好测试一下,它是如何占用内存,什么时候开始存入temp目录,怎么迁移到upload目录底下的

文件上传的时候,如果一个上传的文件小于2.5兆,Django会将上传的东西放在内存里,如果上传的文件大于2.5M,Django将整个上传的文件写到一个临时的文件中,这个文件在临时文件路径中。上传完毕后,将调用View中的_Upload()方法将临时文件夹中的临时文件分块写到上传文件的存放路径下,每块的大小为64K,写完后临时文件将被删除。


UploadedFile.multiple_chunks():如果上传的文件足够大需要分块就返回真。默认的这个值是2.5兆,当然这个值是可以调节的,看下面的UploadedFile.chunks():一个产生器,返回文件的块。如果multiple_chunks()是真的话,你应该在一个循环中使用这个方法,而不是使用read();

在你保存上传的文件之前,数据需要被保存在某些地方。默认呢的,如果一个上传的文件小于2.5兆,Django会将上传的东西放在内存里。这意味着只要从内存读取数据并保存到硬盘上,所以很快。然而,如果一个上传的文件太大,Django将上传的文件写到一个临时的文件中,这个文件在你的临时文件路径中。在Unix-like的平台上意味着你可以预见Django产生一个文件保存为/tmp/tmpzfp6I6.upload的文件。如果这个文件足够大,你可以观察到这个文件的大小在增大。

三个设置改变Django的上传处理行为:
FILE_UPLOAD_MAX_MEMORY_SIZE:以bytes为单位的到内存中的最大大小,。比这个值大的文件将被先存到磁盘上。默认是2.5兆
FILE_UPLOAD_TEMP_DIR:比FILE_UPLOAD_MAX_MEMORY_SIZE大的文件将被临时保存的地方。默认是系统标准的临时路径。
FILE_UPLOAD_PERMISSIONS:如果这个没有给出或者是None,你将获得独立于系统的行为。大多数平台,临时文件有一个0600模式,从内存保存的文件将使用系统标准umask。
django上传文件的方法详解

五 关于json

  1.什么是json

      JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)

      JSON 是轻量级的文本数据交换格式

      JSON 独立于语言 *

        JSON 具有自我描述性,更易理解

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

     

  object和python的dict类型是差不多的,但是要求里面必须是双引号,string和list、tuple等也是一样的,都是双引号。python中的datetime等时间日期类型是不能进行json序列化的,因为json没有对应的格式。

              

  前端ajax拿到后端返回的一个python的json模块序列化之后的一个json字符串,那么js通过自己的json接口,将接受到的json字符串来反序列化为js自己语言能够识别的数据类型,然后再进行操作。      

  相当于我有一个json方法,你有一个json方法,你给我发数据必须是json字符串的格式,那么你就需要将你的数据类型序列化为json的字符串,那么序列化的时候,就把你的数据序列化为了符合json标准的字符串,然后我接收到这个字符串之后,我通过我的json方法,将数据转换为我的语言支持的数据类型。在进行反序列化的时候,如果你的字符串不符合json的格式,那么反序列化的时候就会报错,所以只要你是通过json序列化成的字符串,都是能够json反序列化的,因为json序列化的时候,就把你的数据改为了符合json标准的字符串形式,例如:里面的单引号,序列化后变成了双引号。

  json对象的判断:

json对象:

["one", "two", "three"] { "one": 1, "two": 2, "three": 3 } #这就是一个json的object类型,符合json的标准格式,就可以通过dumps来进行序列化 {"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字符串,在进行序列化的时候的区别

import json
# s = "{'name':'chao','age':18}" #普通字符串,每加引号的没问题,加了引号的,必须是双引号才能使用json.loads()。
s = '{"name":"chao","age":18}'   #json字符串,里面必须是双引号
ret = json.loads(s)
print(ret)
print(ret['name'])

  2.js的stringify与parse方法

    JavaScript中关于JSON对象和字符串转换的两个方法:

    JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象 

JSON.parse('{"name":"chao"}');
JSON.parse('{name:"chao"}') ;   // 错误  
JSON.parse('[18,undefined]') ;   // 错误  不符合属性名必须加双引号

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

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

  3.和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>
xml数据

    用JSON表示如下:

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

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

 4.ajax和服务端的数据交互时的序列化问题

  当我们给ajax回复的不是一个字符串,而是其他数据类型的时候,需要我们将数据转换为json字符串进行发送,这样好配合js进行json字符串的处理,不然发送或者接受的是普通字符串的话,没办法处理成原来的数据类型。

  这就用到了我们前面的视图函数中那个JsonResponse了,看博客,里面response的部分

  还要注意ajax中的data参数:

    data参数中的键值对,如果值值不为字符串,需要将其转换成字符串类型。

$("#b1").on("click", function () {
    $.ajax({
      url:"/ajax_add/",
      type:"GET",
      data:{"i1":$("#i1").val(),"i2":$("#i2").val(),"hehe": JSON.stringify([1, 2, 3])},
      success:function (data) {
        $("#i3").val(data);
      }
    })
  })

 5.Django内置的serializers做序列化

不咋用
def books_json(request): book_list = models.Book.objects.all()[0:10] from django.core import serializers ret = serializers.serialize("json", book_list) return HttpResponse(ret)

 六 补充一个SweetAlert插件示例

 看效果:

    

 

  点击下载Bootstrap-sweetalert项目

$(".btn-danger").on("click", function () {
  swal({
    title: "你确定要删除吗?",
    text: "删除可就找不回来了哦!",
    type: "warning",
    showCancelButton: true,
    confirmButtonClass: "btn-danger",
    confirmButtonText: "删除",
    cancelButtonText: "取消",
    closeOnConfirm: false
    },
    function () {
      var deleteId = $(this).parent().parent().attr("data_id");
      $.ajax({
        url: "/delete_book/",
        type: "post",
        data: {"id": deleteId},
        success: function (data) {
          if (data.status === 1) {
            swal("删除成功!", "你可以准备跑路了!", "success");
          } else {
            swal("删除失败", "你可以再尝试一下!", "error")
          }
        }
      })
    });
})

 七 同源策略与Jsonp

  同源策略

    同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

    同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

  示例:

   项目1:    
==================================http://127.0.0.1:8001项目的index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>


<button>ajax</button>
{% csrf_token %}

<script>
    $("button").click(function(){


        $.ajax({
            url:"http://127.0.0.1:7766/SendAjax/",
            type:"POST",
            data:{"username":"yuan","csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val()},
            success:function(data){
                alert(123);
                alert(data)
            }
        })
    })
</script>
</body>
</html>


==================================http://127.0.0.1:8001项目的views

def index(request):


    return render(request,"index.html")


def ajax(request):
    import json
    print(request.POST,"+++++++++++")
    return HttpResponse(json.dumps("hello"))
 
      项目2:
==================================http://127.0.0.1:8002项目的index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>


<button>sendAjax</button>

{% csrf_token %}

<script>
    $("button").click(function(){


        $.ajax({
            url:"/SendAjax/",
            type:"POST",
            data:{"username":"yuan","csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val()},
            success:function(data){
                alert(data)
            }
        })
    })
</script>

</body>
</html>


==================================http://127.0.0.1:8002项目的views

def index(request):

    return render(request,"index.html")

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt


def SendAjax(request):

    import json

    print("++++++++")

    return HttpResponse(json.dumps("hello2"))

 

      当点击项目1的按钮时,发送了请求,但是会发现报错如下:
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:7766/SendAjax/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

      但是注意,项目2中的访问已经发生了,说明是浏览器对非同源请求返回的结果做了拦截。

  Jsonp

    jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。

    思考:这算怎么回事?

<script src="http://code.jquery.com/jquery-latest.js"></script>

 

    借助script标签,实现跨域请求,示例:

复制代码
# =============================http://127.0.0.1:8001/index


<button>ajax</button>
{% csrf_token %}

<script>
    function func(name){
        alert(name)
    }
</script>

<script src="http://127.0.0.1:7766/SendAjax/"></script>


# =============================http://127.0.0.1:8002/
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt


def SendAjax(request):

    import json

    print("++++++++")
    # dic={"k1":"v1"}
    return HttpResponse("func('yuan')")  # return HttpResponse("func('%s')"%json.dumps(dic))
复制代码

    这其实就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。

    将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。

        一般情况下,我们希望这个script标签能够动态的调用,而不是像上面因为固定在html里面所以没等页面显示就执行了,很不灵活。我们可以通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务了。

复制代码
<button onclick="f()">sendAjax</button>

<script>
    function addScriptTag(src){
         var script = document.createElement('script');
         script.setAttribute("type","text/javascript");
         script.src = src;
         document.body.appendChild(script);
         document.body.removeChild(script);
    }


    function func(name){
        alert("hello"+name)
    }

    function f(){
         addScriptTag("http://127.0.0.1:7766/SendAjax/")
    }
</script>
复制代码

    为了更加灵活,现在将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调:

    将8001的f()改写为:

function f(){
         addScriptTag("http://127.0.0.1:7766/SendAjax/?callbacks=func")
    }

 

    8002的views改为:

def SendAjax(request):
 
    import json
 
    dic={"k1":"v1"}
 
    print("callbacks:",request.GET.get("callbacks"))
    callbacks=request.GET.get("callbacks")
 
    return HttpResponse("%s('%s')"%(callbacks,json.dumps(dic)))

 

  jQuery对JSONP的实现

    getJSON

      jQuery框架也当然支持JSONP,可以使用$.getJSON(url,[data],[callback])方法

      8001的html改为:

复制代码
<button onclick="f()">sendAjax</button>

<script>

    function f(){
          $.getJSON("http://127.0.0.1:7766/SendAjax/?callbacks=?",function(arg){
            alert("hello"+arg)
        });
    }
    
</script>
复制代码

      8002的views不改动。

      结果是一样的,要注意的是在url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数名。

          此外,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?我们可以使用$.ajax方法来实现

    $.ajax

      8001的html改为:

复制代码
<script>

    function f(){
          $.ajax({
                url:"http://127.0.0.1:7766/SendAjax/",
                dataType:"jsonp",
                jsonp: 'callbacks',
                jsonpCallback:"SayHi"
           });

       }

    function SayHi(arg){
                alert(arg);
            }

</script>
复制代码

      8002的views不改动。

      当然,最简单的形式还是通过回调函数来处理:

复制代码
<script>

    function f(){

            $.ajax({
               url:"http://127.0.0.1:7766/SendAjax/",
               dataType:"jsonp",            //必须有,告诉server,这次访问要的是一个jsonp的结果。
               jsonp: 'callbacks',          //jQuery帮助随机生成的:callbacks="wner"
               success:function(data){
                   alert("hi "+data)
              }
         });

       }

</script>
复制代码

      jsonp: 'callbacks'就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名'SayHi',server端接受callback键对应值后就可以在其中填充数据打包返回了; 

      jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,那前端就得用回调函数来处理对应数据了。利用jQuery可以很方便的实现JSONP来进行跨域访问。  

      注意 JSONP一定是GET请求

  应用:

<input type="button" onclick="AjaxRequest()" value="跨域Ajax" />


<div id="container"></div>


    <script type="text/javascript">
        function AjaxRequest() {
            $.ajax({
                url: 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403',
                type: 'GET',
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallback: 'list',
                success: function (data) {
                    
                    $.each(data.data,function(i){
                        var item = data.data[i];
                        var str = "<p>"+ item.week +"</p>";
                        $('#container').append(str);
                        $.each(item.list,function(j){
                            var temp = "<a href='" + item.list[j].link +"'>" + item.list[j].name +" </a><br/>";
                            $('#container').append(temp);
                        });
                        $('#container').append("<hr/>");
                    })

                }
            });
        }
</script>

 八 CORS

  一、简介

    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

    整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

    因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

  二、两种请求

    浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

    只要同时满足以下两大条件,就属于简单请求。

复制代码
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
复制代码

    凡是不同时满足上面两个条件,就属于非简单请求。

    浏览器对这两种请求的处理,是不一样的。

复制代码
* 简单请求和非简单请求的区别?

   简单请求:一次请求
   非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers
复制代码

 

  支持跨域,简单请求

    服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'

  支持跨域,复杂请求

    由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

 

posted @ 2019-05-15 20:33  与凯学习  阅读(225)  评论(0编辑  收藏  举报