• Django中的事务与ajax


    事务与锁

     1.行级锁

    行级锁是由存储引擎实现的。如mysql里默认指定的InnoDB存储引擎,由它实现行级锁。InnoDB的行级锁定同样分为两种类型,共享锁(X)和排他锁(S)。

    对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁

    事务可以通过以下语句显示给记录集加共享锁或排他锁。

    共享锁(S):SELECT * FROM table_name WHERE .. LOCK IN SHARE MODE

    排他锁(X):SELECT * FROM table_name WHERE .. FOR UPDATE

    修改时,加了排他锁,其他临时会话不能查和修改等操作

    Lock table tab_name write;给表加了写锁,临时会话不能往里面写。

    Unlock tables;    解除写锁

     2.ORM里加锁

    加行级锁:select_for_update(nowait=False, skip_locked=False)

    加行级锁必须用在事务里。

    手动加互斥锁:

    entries = models.Entry.objects.select_for_update().filter(author=request.user)

    共享锁Mysql会自动加,因此不需用加共享锁。create、update、delete操作时,mysql自动加行级互斥锁,因此查看时可加互斥锁。

    所有匹配的行将被锁定,直到事务结束。这意味着可以通过锁防止数据被其它事务修改。

     3.事务

    (1)事务简介

    事务具有ACID属性

    事务控制语句

    BEGIN或START TRANSACTION;显式地开启一个事务;
    
    COMMIT;               COMMIT会提交事务,并使已对数据库进行的所有修改成为永久性的;
    
    ROLLBACK;            回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
    
    SAVEPOINT identifier;    SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT;
    
    RELEASE SAVEPOINT identifier;    删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
    
    ROLLBACK TO identifier;   把事务回滚到标记点;

    (2Django里事务操作

    Django里开启事务有两大类,一是全局形式,二是局部形式。

    a.全局形式

    方法:在用户settings.py文件里,在DATABASES配置里,加入 "ATOMIC_REQUESTS":True。

    原理:当有请求过来时,Django会在调用视图方法前开启一个事务。如果请求正确处理并正确返回了结果,Django就会提交该事务。否则,Django会回滚该事务。因此全局开启事务,绑定的是http请求响应整个过程。

    b.局部形式

    atomic(using=None, savepoint=True)[source]

    atomic是原子性。使用atomic,我们就可以创建一个具备原子性的代码块。一旦代码块正常运行完毕,所有的修改会被提交到数据库。反之,如果有异常,更改会被回滚。(要么全成功,要么全失败)

    (i)方式一:装饰器

    from django.db import transaction
    @transaction.atomic
    def viewfunc(request):    do_stuff() 里面代码就在事务里

    (ii)方式二:上下文管理器(设置事务的保存点)

    from django.db import transaction
    def  viewfunc(request):
         do_stuff()       #这段代码使用默认的方式
         with transaction.atomic():   #保存点
                do_more_stuff()      #这段代码在事务里
        do_other_stuff()

     注意::如果配置了全局的事务,它和局部事务可能会产生冲突。比如说:局部的事务完成之后,如果该函数里面其他的sql出了问题,那么局部事务也是提交不上。因为全局会回滚这个请求和响应所涉及到的所有的sql(局部的也被回滚了)。建议项目用局部事务。

     设置事务小原则:

    1.保持事务短小

    2.尽量避免事务中rollback

    3.尽量避免savepoint

    4.默认情况下,依赖于悲观锁

    5.为吞吐量要求苛刻的事务考虑乐观锁

    6.显示声明打开事务

    .锁的行越少越好,锁的时间越短越好

    Ajax使用

    一、Ajax介绍

    Ajax:Asynchronous Javascript And XML。即异步的Javascript和XML。使用Javascript语言与服务器进行异步交互,传输的数据为XML(现在多为json)

    (一)Ajax两大特性:

    异步交互和局部刷新。

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

    局部刷新:不会刷新整个页面,局部内容发生变化。

    (二)应用场景

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

    2.注册时候的用户名的查重。(提示该用户已经注册过了)

    当输入用户名后,光标移动到其他表单项时,浏览器会使用AJAX技术向服务器发出请求(这个时候你可以继续其他操作,这就是异步交互),服务器会查询用户名是否存在,然后返回结果(这个页面没有刷新,只有局部内容刷新了)。

    二、Ajax操作

    做个简单的注册页面,利用Ajax实现异步请求和局部刷新

    (一)django基本设置

    静态文件配置(引入juqery.js),分发路由操作

    (二)操作

    1.回复html页面

    2.写html文件

    ajax上传文件:<input type="file" id="file">
    
    ajax用户名:<input type="text" id="uname">
    
    <button id="btn">提交</button>

    注意:form表单最后的提交按钮的type必须时button。如果还是写submit,那么提交还是按照form表单提交,不按照ajax提交。提交方式与form表单无关了,自然服务器取数据也与input标签的name值无关了。

    3.ajax代码接收用户数据

    1)绑定事件

    $('#btn').click(function () {}

    给提交按钮绑定点击事件。

    2)ajax

    通过 $.ajax({})方式来使用ajax。{}里是字典,主要包括三大部分url、type和success。浏览器有数据时还需data获取数据。

    data内容:

    data:{
         uname:$('#username').val(),    {#  获取数据  #}
         pwd:$('#password').val(),
         //csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val(), {#和form表单一样的crsf #}
         //方式二:
         csrfmiddlewaretoken: '{{ csrf_token }}'
                },

    data数据是字典的形式。键为后台获取值的关键字。值:先定位到html文件里数据的标签,再通过val()取值。data数据里还要给后台传输csrf值(post请求提交要验证):html标签里有,直接以键值对的形式传输过去。

    4.后台处理数据并发送数据给ajax

    Ajax将data数据发给后台,后台在视图函数进行处理:

    1)获取ajax发来的数据

    name=request.POST.get('uname')
    
    pwd=request.POST.get('pwd')

    2)匹配成功---进行数据处理和给ajax发消息

    根据取来的值和数据库的进行比较,如果再数据库里,就给用户返回首页。不用ajax的话,django就直接重定向redirect。但用ajax,ajax识别不了redirect。因此要给ajax回复一个字典,让ajax进行重定向。

    字典里有code码。方便ajax做判断,再做不同的处理。

    发送形式:django发送给浏览器只有两种。Render发网页,HttpResponse发字符串。因此发过去的字典必须是字符串形式的。而ajax那边需要的是字典形式的数据。因此需要用到json解析。

    数据传输

    方式一:(不推荐)

    发送端:字典外面直接包裹‘’,写成字符串的形式(注意json格式。里面都是双引号,最外面单引号),然后通过HttpResponse发送过去

     ret = '{"code":0,"redirect_url":"/index/"}'
     return HttpResponse(ret)

    接收端:JSON.parse(接收数据)  去掉’’,由字符串变成字典

     var resStr=JSON.parse(res);

    res是响应数据,从success:function (res)里接收

    方式二:

    dumps方法将ret转为json字符串

    d1={"code":0,"redirect_url":"/index/"}
    import json
    d1_json=json.dumps(d1)     #给字典外面加个单引号,里面都是双引号 return HttpResponse(d1_json,content_type='application/json')    #声明数据是json格式的,ajax接收数据不用反序列化。

    HttpResponse发送json字符串过去时,可以再后面告诉ajax这个数据的格式是json格式的,ajax接收数据不用反序列化,可以直接处理。

    方式三:

    利用Django的JsonResponse方法。

    from django.http import JsonResponse
    d1 = {"code": 0, "redirect_url": "/index/"}
    return JsonResponse(d1)

     JsonResponse内部有转化json字符串的功能,同时也声明了类型是json。因此直接传输数据给ajax。Ajax也不需要反序列化。

    3)匹配失败:

    匹配失败,也需告诉ajax信息,让ajax操作。

    ret = '{"code":3,"redirect_url":"用户名或者密码错误"}'
    return HttpResponse(ret)

     5.ajax处理后台的数据,并操作dom标签

    Ajax接收到后台的数据,并进行处理,这部分代码都写在success:function (res) {}字典里。参数res是后台的响应数据,即传过来的值。

    1)针对不同code做不同的处理

    如果是code是3,即用户名或密码错误,就新增标签给用户提示。(标签这个也可以写在html里。)

     success:function (res) {
                    console.log(res);          {#res为响应数据#}
                    var resStr=JSON.parse(res);    {#  反序列化 解析res,由字符串变为字典  直接传过来json字符串需要,用dumps序列化并且发送过来声明类型了或者是JsonResponse发送过来不需要反序列化#}
                    if (resStr['code']===3){
                        {#局部刷新:'{"code":3,"redirect_url":"用户名或者密码错误"}'#}
                        var spanEle=document.createElement('span');
                        $(spanEle).text(resStr['redirect_url']);
                        $('form').append(spanEle);
                    }

    2)如果输入正确,即code=0,就重定向

     location.href=resStr['redirect_url'];

    (三)Ajax补充:

    1.ajax流程

    Ajax代码可以通过外部文件引入的方式导入到html文件。但这有一个问题:加载顺序会导致ajax里的模板渲染失败({{}}和{%%})。

    地址栏输入网址------》django进行路由分发------》执行逻辑函数--------》渲染html文件---------》把渲染好的html给浏览器渲染------》浏览器再次渲染。

     如果ajax文件是通过外部方式导入的,django在模板渲染时是渲染不到ajax的。浏览器渲染HTML文件时才加载ajex文件,此时django已经渲染结束了。

    解决方法:ajax是外部文件时,js语法就不要用django的模板语法。

    2.ajax的csrf_token验证

    三个方式:(1)通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。(2)模板语法。在ajax设置时发送数据过去。(3)通过获取返回的cookie中的字符串 放置在请求头中发送。

    1)"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()

    2)$.ajaxSetup({

        data: {csrfmiddlewaretoken: '{{ csrf_token }}' },

    });

     三、Ajax文件上传

    (一)ContentType介绍

    1.简介

    ContentType可在请求头中看到,指的是请求体的编码类型。表示的是数据提交的方式。

    ContentType类型有三种:application/x-www-form-urlencoded、 multipart/form-data和 application/json。

    2.application/x-www-form-urlencoded

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

    数据形式:以&作为分割,通过等于方式连接键和值

      3.multipart/form-data

    上传文件

    使用表单上传文件时,必须让 <form> 表单的 enctype 等于 multipart/form-data

     4.application/json

    Network里可以查看到response headers和requests headers。response headers的contenttype是服务器告诉浏览器数据是什么格式封装的。requests headers的contenttype是浏览器告诉服务器,数据是什么格式。

    5.ajax里的contenttype

    ajax里面写上这个contenttype类型,表示data的数据类型也要变成这个格式。Data默认是urlencoded格式。服务端接受到数据之后,通过contenttype类型的值来使用不同的方法解析数据。django能解析application/x-www-form-urlencoded 和multipart/form-data,但不能解析application/json类型。

     Django里可通过request.body取出原始值,再将byte类型的数据转化为字符串。

     (二)ajax文件上传

    1.操作

    Ajax给服务端上传文件的数据不能直接写在data里了,要借助一个FormData的类生成的对象formdata,把文件添加到这个对象中,在通过data发送给服务端。其次,还要在data下面写两个参数,保证ajax上传文件时不对文件内容做任何处理。

     1)把数据添加到formdata对象里

    var formdata=new FormData();
    formdata.append('aa',$('#uname').val());
    formdata.append('headpic',$('#file')[0].files[0]);
    formdata.append('csrfmiddlewaretoken',$('[name=csrfmiddlewaretoken]').val());

    找到文件操作: $('#file')[0]将jquery对象变为dom对象,利用dom的files方法拿到文件(是个列表),再取第一个值,即是文件内容。

    2)将formdata放到data里,提交给服务端。并且设置参数

    data:formdata,
    processData:false,    //不处理数据(不让预处理)
    contentType:false,   //不设置内容类型    这两个在ajax上传文件是固定死的.不对文件做任何操作

    3)视图函数最开始要返回页面

        if request.method=='GET':
            return render(request,'upload.html')

    4)取到数据

    file_obj=request.FILES.get('headpic')

    文件必须通过FILES方法取得,file_obj里就有数据。调用该对象的name属性,获得文件名。

    5)写入数据

    方式一:   

      with open(path,'wb')as f:
                for i in file_obj:     #每次读一行(以
       
       
    为分隔符)
                    f.write(i)

    方式二:

    with open(path, 'wb')as f:
                 for chunk in file_obj.chunks():    #读数据有个固定大小    默认64KB 最大2.5M .django的全局配置里面可以改chunks大小
                    f.write(chunk)           # 64 * 2 ** 10  在base里
  • 相关阅读:
    自由职业一时爽,一直自由一直爽
    N+6 裁员裁出幸福感的背后
    你真的了解 Cookie 和 Session 吗?
    百亿级企业级 RPC 框架开源了!
    做一个有脑子的程序员
    Java 生态核心知识点整理
    关于第三方支付,看这篇文章就够了!
    程序员该不该主动提加薪?
    我是为何下定决心入行程序员的
    老程序员被新程序员拍在沙滩上?
  • 原文地址:https://www.cnblogs.com/yq055783/p/12370397.html
Copyright © 2020-2023  润新知