我的编程空间,编程开发者的网络收藏夹
学习永远不晚

day 71 Django基础六之ORM

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

day 71 Django基础六之ORM

 

 

本节目录

  • 一 锁
  • 二 事务
  • 三 xxx
  • 四 xxx
  • 五 xxx
  • 六 xxx
  • 七 xxx
  • 八 xxx

一 锁

  行级锁

    select_for_update(nowait=False, skip_locked=False) #注意必须用在事务里面,至于如何开启事务,我们看下面的事务一节。

    返回一个锁住行直到事务结束的查询集,如果数据库支持,它将生成一个 SELECT ... FOR UPDATE 语句。

    举个例子:

entries = Entry.objects.select_for_update().filter(author=request.user)  #加互斥锁,由于mysql在查询时自动加的是共享锁,所以我们可以手动加上互斥锁。create、update、delete操作时,mysql自动加行级互斥锁

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

    一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。 如果这不想要使查询阻塞的话,使用select_for_update(nowait=True)。 如果其它事务持有冲突的锁,互斥锁, 那么查询将引发 DatabaseError 异常。你也可以使用select_for_update(skip_locked=True)忽略锁定的行。 nowait和  skip_locked是互斥的,同时设置会导致ValueError。

    目前,postgresql,oracle和mysql数据库后端支持select_for_update()。 但是,MySQL不支持nowait和skip_locked参数。

    使用不支持这些选项的数据库后端(如MySQL)将nowait=True或skip_locked=True转换为select_for_update()将导致抛出DatabaseError异常,这可以防止代码意外终止。

  表锁(了解)

复制代码
class LockingManager(models.Manager):
    """ Add lock/unlock functionality to manager.

    Example::

        class Job(models.Model): #其实不用这么负载,直接在orm创建表的时候,给这个表定义一个lock和unlock方法,借助django提供的connection模块来发送锁表的原生sql语句和解锁的原生sql语句就可以了,不用外层的这个LckingManager(model.Manager)类

            manager = LockingManager()

            counter = models.IntegerField(null=True, default=0)

            @staticmethod
            def do_atomic_update(job_id)
                ''' Updates job integer, keeping it below 5 '''
                try:
                    # Ensure only one HTTP request can do this update at once.
                    Job.objects.lock()

                    job = Job.object.get(id=job_id)
                    # If we don't lock the tables two simultanous
                    # requests might both increase the counter
                    # going over 5
                    if job.counter < 5:
                        job.counter += 1                                        
                        job.save()

                finally:
                    Job.objects.unlock()


    """    

    def lock(self):
        """ Lock table. 

        Locks the object model table so that atomic update is possible.
        Simulatenous database access request pend until the lock is unlock()'ed.

        Note: If you need to lock multiple tables, you need to do lock them
        all in one SQL clause and this function is not enough. To avoid
        dead lock, all tables must be locked in the same order.

        See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
        """
        cursor = connection.cursor()
        table = self.model._meta.db_table
        logger.debug("Locking table %s" % table)
        cursor.execute("LOCK TABLES %s WRITE" % table)
        row = cursor.fetchone()
        return row

    def unlock(self):
        """ Unlock the table. """
        cursor = connection.cursor()
        table = self.model._meta.db_table
        cursor.execute("UNLOCK TABLES")
        row = cursor.fetchone()
        return row  
复制代码

 

 

二 事务

  

   关于MySQL的事务处理,我的mysql博客已经说的很清楚了,那么我们来看看Django是如果做事务处理的。django1.8版本之前是有很多种添加事务的方式的,中间件的形式(全局的)、函数装饰器的形式,上下文管理器的形式等,但是很多方法都在1.8版之后给更新了,下面我们只说最新的:

  1 全局开启

    在Web应用中,常用的事务处理方式是将每个请求都包裹在一个事务中。这个功能使用起来非常简单,你只需要将它的配置项ATOMIC_REQUESTS设置为True。

    它是这样工作的:当有请求过来时,Django会在调用视图方法前开启一个事务。如果请求却正确处理并正确返回了结果,Django就会提交该事务。否则,Django会回滚该事务。

复制代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mxshop',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': '123',
        'OPTIONS': {
            "init_command": "SET default_storage_engine='INNODB'",
       #'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", #配置开启严格sql模式


        }
        "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程
"AUTOCOMMIT":False, #全局取消自动提交,慎用 },
  'other':{
    'ENGINE': 'django.db.backends.mysql', 
            ......
  } #还可以配置其他数据库
}
复制代码

    上面这种方式是统一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败)。是全局性的配置, 如果要对某个http请求放水(然后自定义事务),可以用non_atomic_requests修饰器,那么他就不受事务的管控了

复制代码
from django.db import transaction

@transaction.non_atomic_requests
def my_view(request):
    do_stuff()

@transaction.non_atomic_requests(using='other')
def my_other_view(request):
    do_stuff_on_the_other_database()
复制代码

    但是Django 文档中说,不推荐这么做。因为如果将事务跟 HTTP 请求绑定到一起的时,然而view 是依赖于应用程序对数据库的查询语句效率和数据库当前的锁竞争情况。当流量上来的时候,性能会有影响,知道一下就行了

    所以推荐用下面这种方式,通过 transaction.atomic 来更加明确的控制事务。atomic允许我们在执行代码块时,在数据库层面提供原子性保证。 如果代码块成功完成, 相应的变化会被提交到数据库进行commit;如果执行期间遇到异常,则会将该段代码所涉及的所有更改回滚。

  2 局部使用事务

    atomic(using=None, savepoint=True)[source]  ,参数:using='other',就是当你操作其他数据库的时候,这个事务才生效,看上面我们的数据库配置,除了default,还有一个other,默认的是default。savepoint的意思是开启事务保存点,推荐看一下我数据库博客里面的事务部分关于保存点的解释。

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

    被atomic管理起来的代码块还可以内嵌到方法中。这样的话,即便内部代码块正常运行,如果外部代码块抛出异常的话,它也没有办法把它的修改提交到数据库中。

    用法1:给函数做装饰器来使用 

from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

 

    用法2:作为上下文管理器来使用,其实就是设置事务的保存点

复制代码
from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()

    with transaction.atomic():   #保存点
        # This code executes inside a transaction.
        do_more_stuff()

    do_other_stuff()
复制代码

      一旦把atomic代码块放到try/except中,完整性错误就会被自然的处理掉了,比如下面这个例子:

复制代码
from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()
复制代码

    用法3:还可以嵌套使用,函数的事务嵌套上下文管理器的事务,上下文管理器的事务嵌套上下文管理器的事务等。下面的是函数嵌套上下文的例子:

复制代码
from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
       #other_task() #还要注意一点,如果你在事务里面写了别的操作,只有这些操作全部完成之后,事务才会commit,也就是说,如果你这个任务是查询上面更改的数据表里面的数据,那么看到的还是事务提交之前的数据。 except IntegrityError: handle_exception() add_children()
复制代码

      这个例子中,即使generate_relationships()中的代码打破了数据完整性约束,你仍然可以在add_children()中执行数据库操作,并且create_parent()产生的更改也有效。需要注意的是,在调用handle_exception()之前,generate_relationships()中的修改就已经被安全的回滚了。因此,如果有需要,你照样可以在异常处理函数中操作数据库。

复制代码
尽量不要在atomic代码块中捕获异常

  因为当atomic块中的代码执行完的时候,Django会根据代码正常运行来执行相应的提交或者回滚操作。如果在atomic代码块里面捕捉并处理了异常,就有可能隐盖代码本身的错误,从而可能会有一些意料之外的不愉快事情发生。

  担心主要集中在DatabaseError和它的子类(如IntegrityError)。如果这种异常真的发生了,事务就会被破坏掉,而Django会在代码运行完后执行回滚操作。如果你试图在回滚前执行一些数据库操作,Django会抛出TransactionManagementError。通常你会在一个ORM相关的信号处理器抛出异常时遇到这个行为。

捕获异常的正确方式正如上面atomic代码块所示。如果有必要,添加额外的atomic代码块来做这件事情,也就是事务嵌套。这么做的好处是:当异常发生时,它能明确地告诉你那些操作需要回滚,而那些是不需要的。

复制代码

    为了保证原子性,atomic还禁止了一些API。像试图提交、回滚事务,以及改变数据库连接的自动提交状态这些操作,在atomic代码块中都是不予许的,否则就会抛出异常。

  下面是Django的事务管理代码:

  • 进入最外层atomic代码块时开启一个事务;
  • 进入内部atomic代码块时创建保存点;
  • 退出内部atomic时释放或回滚事务;注意如果有嵌套,内层的事务也是不会提交的,可以释放(正常结束)或者回滚
  • 退出最外层atomic代码块时提交或者回滚事务;

     你可以将保存点参数设置成False来禁止内部代码块创建保存点。如果发生了异常,Django在退出第一个父块的时候执行回滚,如果存在保存点,将回滚到这个保存点的位置,否则就是回滚到最外层的代码块。外层事务仍然能够保证原子性。然而,这个选项应该仅仅用于保存点开销较大的时候。毕竟它有个缺点:会破坏上文描述的错误处理机制。

   注意:transaction只对数据库层的操作进行事务管理,不能理解为python操作的事务管理

复制代码
def example_view(request):
    tag = False
    with transaction.atomic():
        tag = True
        change_obj() # 修改对象变量
        obj.save()
        raise DataError
    print("tag = ",tag) #结果是True,也就是说在事务中的python变量赋值,即便是事务回滚了,这个赋值也是成功的
复制代码

  还要注意:如果你配置了全局的事务,它和局部事务可能会产生冲突,你可能会发现你局部的事务完成之后,如果你的函数里面其他的sql除了问题,也就是没在这个上下文管理器的局部事务包裹范围内的函数里面的其他的sql出现了问题,你的局部事务也是提交不上的,因为全局会回滚这个请求和响应所涉及到的所有的sql,所以还是建议以后的项目尽量不要配置全局的事务,通过局部事务来搞定,当然了,看你们的业务场景。

   transaction的其他方法

复制代码
@transaction.atomic
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()  #创建保存点

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid) #提交保存点
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)  #回滚保存点
      # open transaction now contains only a.save()

  transaction.commit() #手动提交事务,默认是自动提交的,也就是说如果你没有设置取消自动提交,那么这句话不用写,如果你配置了那个AUTOCOMMIT=False,那么就需要自己手动进行提交。
复制代码

 

   为保证事务的隔离性,我们还可以结合上面的锁来实现,也就是说在事务里面的查询语句,咱们使用select_for_update显示的加锁方式来保证隔离性,事务结束后才会释放这个锁,例如:(了解)

复制代码
@transaction.atomic ## 轻松开启事务
def handle(self):
    ## 测试是否存在此用户
    try:
        ## 锁定被查询行直到事务结束
        user = 
    User.objects.select_for_update().get(open_id=self.user.open_id)
        #other sql 语句
    except User.DoesNotExist:
        raise BaseError(-1, 'User does not exist.')
    
复制代码

 

   通过Django外部的python脚本来测试一下事务:

复制代码
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
    import django
    django.setup()

    import datetime
    from app01 import models

    try:
        from django.db import transaction
        with transaction.atomic():
            new_publisher = models.Publisher.objects.create(name="火星出版社")
            models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10)  # 指定一个不存在的出版社id
    except Exception as e:
        print(str(e))
复制代码

 

   下面再说一些设置事务的小原则吧:

    1.保持事务短小 
    2.尽量避免事务中rollback 
    3.尽量避免savepoint 
    4.默认情况下,依赖于悲观锁 
    5.为吞吐量要求苛刻的事务考虑乐观锁 
    6.显示声明打开事务 
    7.锁的行越少越好,锁的时间越短越好

 

三 xxx

 

 

 

 

 

四 xxx

 

 

 

 

 

五 xxx

 

 

 

 

 

 

六 xxx

 

 

 

 

七 xxx

 

 

 

 

八 xxx

  

 

 

 

 

 

回到顶部

 
 
 
 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

day 71 Django基础六之ORM

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

day 71 Django基础六之ORM

本节目录一 锁二 事务三 xxx四 xxx五 xxx六 xxx七 xxx八 xxx一 锁  行级锁    select_for_update(nowait=False, skip_locked=False) #注意必须用在事务里面,至于如何
2023-01-31

day 52 Django基础一之web

django第一天本节目录一 web框架的本质及自定义web框架二 模板渲染JinJa2三 MVC和MTV框架四 Django的下载安装五 基于Django实现的一个简单示例一 web框架的本质及自定义web框架  我们可以这样理解:所有的
2023-01-31

day 63 Django基础九之中间件

本节目录一 前戏二 中间件介绍三 自定义中间件四 中间件的执行流程五 中间件版登陆认证六 xxx七 xxx八 xxx一 前戏    我们在前面的课程中已经学会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面。我们
2023-01-31

Django基础——ORM字段和字段参数

ORM概念:对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象(1. 不同的程序员写的SQL水平参差不齐2. 执行效率也参差不齐)的技术。ORM 能够把
2023-01-31

Django基础之(十)DRF

简介官方文档RequirementsREST framework requires the following:Python (2.7, 3.4, 3.5, 3.6, 3.7)Django (1.11, 2.0, 2.1)The follo
2023-01-31

一篇文章带你了解Django ORM操作(基础篇)

在日常开发中,需要大量对数据库进行增删改查操作。如果头铁的话,使用原生SQL是最好的,毕竟性能又高,又灵活。
基础DjangoORM2024-12-03

web开发框架之Django基础

在脚本中如何进行Django的运行if __name__ == '__main__': import os import django # 注意路径(当前所在的位置,要加载Django的配置文件) os.enviro
2023-01-30

Python全栈开发之Django基础

No.1 MVC&MTVMVCM全拼为Model,主要封装对数据库层的访问,对数据库中的数据进行增、删、改、查操作V全拼为View,用于封装结果,生成页面展示的html内容C全拼为Controller,用于接收请求,处理业务逻辑,与Mode
2023-01-31

Rust编程基础之六大数据类型

程序在索引操作中使用一个无效的值时导致 运行时 错误。程序带着错误信息退出,并且没有执行最后的 println! 语句。当尝试用索引访问一个元素时,Rust 会检查指定的索引是否小于数组的长度。

django基础之数据库操作方法(详解)

Django 自称是“最适合开发有限期的完美WEB框架”。本文参考《Django web开发指南》,快速搭建一个blog 出来,在中间涉及诸多知识点,这里不会详细说明,如果你是第一次接触Django ,本文会让你在感性上对Django有个认
2022-06-04

(Redis基础教程之六)如何使用Redis中的List

如何在ubuntu18.04上安装和保护redis如何连接到Redis数据库如何管理Redis数据库和Keys如何在Redis中管理副本和客户端如何在Redis中管理字符串如何在Redis中管理list如何在Redis中管理Hashes如何在Redis中管理S
(Redis基础教程之六)如何使用Redis中的List
2017-01-10

思科路由器基础知识之六种命令状态

  大家都应该知道其实思科路由器配置过程还是比较复杂的,毕竟需要考虑的因素真的有非常的多,特别就是在安全这一个方面。今天小编给大家带来的教程是:思科路由器基础知识之六种命令状态。那么接下来,我们就一起针对这一个问题详细的介绍一下吧。  命令状态一:router(config-if)#;router(config-lin
思科路由器基础知识之六种命令状态
2024-04-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录