day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

一.多表的创建

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
from django.db import models

# Create your models here.
class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    au = models.OneToOneField(to='AuthorDetail',to_field='id')  # 一对一表的创建

class AuthorDetail(models.Model):
    id = models.AutoField(primary_key=True)
    address = models.CharField(max_length=32)
    tel = models.CharField(max_length=11)

class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)                                                        # 一对多表的创建
    authors = models.ManyToManyField(to='Author')  #  多对多表的创建  (方法一)  自行创建第三张表  

#
# class Book2Author(models.Model):       #  多对多表的创建  (方法二) ORM的一些功能不能使用
#     id = models.AutoField(primary_key=True)
#     book = models.ForeignKey(to='Book',to_field='id')
#     author = models.ForeignKey(to='Author',to_field='id')
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
①  创建一对一关系字段时的一些参数

to
    设置要关联的表。

to_field
    设置要关联的字段。

on_delete
    同ForeignKey字段。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
②  创建一对多关系字段时的一些参数

to
    设置要关联的表

to_field
    设置要关联的表的字段

related_name
    反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
related_query_name
    反向查询操作时,使用的连接前缀,用于替换表名。

on_delete
    当删除关联表中的数据时,当前表与其关联的行的行为。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
③ 多对多的参数:
    to
        设置要关联的表

    related_name
        同ForeignKey字段。

    related_query_name
        同ForeignKey字段。
    through
        在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

        但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过
    through来指定第三张表的表名。

    through_fields
        设置关联的字段。

    db_table
        默认创建第三张表时,数据库中表的名称。            
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
 ④ .创建表时的一些元信息设置

     ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    class Meta:
        unique_together = ("author", "book")

db_table
    ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。

index_together
    联合索引。

unique_together
    联合唯一索引。

ordering
    指定默认按什么字段排序。

    只有设置了该属性,我们查询到的结果才可以被reverse()。    

创建表时的一些元信息设置
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
⑥  关于on_delete

on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE
删除关联数据,与之关联也删除

models.DO_NOTHING
删除关联数据,引发错误IntegrityError

models.PROTECT
删除关联数据,引发错误ProtectedError

models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

models.SET

删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

关于on_delete参数
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
注意事项:

 1.  表的名称myapp_modelName,是根据 模型中的元数据自动生成的,       也可以覆写为别的名称  
 2.  id 字段是自动添加的
 3. 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
 4.这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,  要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
 5. 定义好模型之后,你需要告诉Django _使用_这些模型。  你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
 6.外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),    你可以赋给它空值 None 。
 7. 咱们的表里面包含了一对一、一对多、多对多的关系,我们基于这几个表来练习,    将来无论有多少张表,都逃脱不了这三个关系,操作起来都是一样的。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

二.  添加表记录(关联表)

  1.多对一表记录的添加

  day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  

  2.一对一表记录的添加 (与多对多相同)

  day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  

  3.多对多表记录的添加

  day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

三.基于对象的跨表查询

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁  

  正向通过 连接字段  ,反向 通过 表(表名小写)

  1.  一对一 查询

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2. 一对多 查询

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  3.多对多  查询

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

查询练习:

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
            外键字段在哪个表中,他找别人就是正向 用属性
            别人找他就是反向   用表名小写

        一对一:
            1 查询名为chao的作者的电话号码
                ret = models.Author.objects.get(name='chao')
                    models.Author.objects.get(name='cc').au.address
                    models.Author.objects.filter(name='cc')[0].au.address
                a = ret.au.tel

            2 查询电话号码为111的作者的名称
                ret = models.AuthorDetail.objects.get(tel=111)
                a = ret.author.name

        一对多:
            3 查询水浒传这本书的出版社名称

                ret = models.Book.objects.get(title='水浒传').publisher.name
            4 查询人民出版社出版了哪些书
                ret = models.Publish.objects.get(name='人民出版社').book_set.all().values('title')

            5 查询在北京地区的出版社出版了哪些书
            ret = models.Publish.objects.get(address='北京').book_set.all().values('title')

        多对多:
            6 三国这本书是哪些作者写的

                ret = models.Book.objects.get(title='三国').authors.all().values('name')

            7 金庸写了哪些书
                ret = models.Author.objects.get(name='金庸').book_set.all().values('title')

            8 年龄为20岁的作者,写了哪些书

                ret = models.Author.objects.get(age=20).book_set.all().values('title')
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

 

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
补充  (从请求数据中取值)

request.POST.get()     取一个指定 值   

request.POST       一个 QueryDict 类型的字典
request.POST.getlist()    多选框 取值时 用列表接收
request.POST.dict()      获取一个包含所有数据的  字典
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

四. 删除  

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
models.Author.objects.filter(id=1).delete()   单表删除  (带级联删除时,可直接删除单表数据)
models.Book.objects.filter(id=1).delete()  
book_obj.authors.clear()   清空对象 所有的关联项
book_obj.authors.remove('2')    指定清除关联项
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

五.更新

models.Book.objects.filter(id=1).update(title='xxxxx',publisher_id=4)                      单表更新
book_obj.authors.set(['3','4'])  #   联表更新(先删除原有的关联,再添加新关联)

六.  基于 下滑线  (效率更快.基本原理是联表操作)

 1.外键字段在哪个表中,他找别人就是正向 用属性
  2.别人找他就是反向   用表名小写

  3.三种方式原理都相同

  4.正向与反向的区别在于: 

    ① 以哪个表为基础 查找

    ② 条件 和 所求结果  与基础表的关系


day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
  一. 一对一操作    #1 查询名为鲁智深的作者的电话号码
    正向
    # ret = models.Author.objects.filter(name='鲁智深').values('au__tel')
    # print(ret) #<QuerySet [{'au__tel': '222'}]>    反向
    # ret = models.AuthorDetail.objects.filter(author__name='鲁智深').values('tel')
    # print(ret) #<QuerySet [{'tel': '222'}]>

二.一对多操作  #4 查询人民出版社出版了哪些书
    正向
    # ret = models.Book.objects.filter(publisher__name='人民出版社').values('title')
    # print(ret)    反向
    # ret = models.Publish.objects.filter(name='人民出版社').values('book__title')
    # print(ret)

    # #5 查询在北京地区的出版社出版了哪些书    正向
    # ret = models.Book.objects.filter(publisher__addr='北京').values('title')
    # print(ret)    反向
    # ret = models.Publish.objects.filter(addr='北京').values('book__title')
    # print(ret)

三  多对多 操作
    # 天龙八部这本书是哪些作者写的
    正向
    # ret = models.Book.objects.filter(title='天龙八部').values('authors__name')
    # print(ret)
      反向    # ret = models.Author.objects.filter(book__title='天龙八部').values('name')
    # print(ret)
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
 三联表  ( 或更多表的查询 )

   # 查询人民出版社出版过的所有书籍的名字以及作者的姓名
    # ret = models.Book.objects.filter(publisher__name='人民出版社').values('title','authors__name')
    # print(ret)
    # ret = models.Publish.objects.filter(name='人民出版社').values('book__title','book__authors__name')
    # print(ret)
    #手机号以2开头的作者出版过的所有书籍名称以及出版社名称
    # ret = models.Book.objects.filter(authors__au__tel__startswith='2').values('title','publisher__name')
    # print(ret)
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

七.  聚合  分组

  1.聚合查询( .aggregate()  )   得到一个字典(结束queryset类型)  

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
Avg()  求平均

Max()   最大值 

Min()    最小值 

count()   计数
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
示例:from django.db.models import Avg, Max, Min
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))  

#count('id'),count(1)也可以统计个数,Book.objects.all().aggregete和Book.objects.aggregate(),都可以
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2.分组查询  (得到一个queryset类型的字典)

  ① 单表分组

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
查询每一个部门名称以及对应的员工数

emp:

id  name age   salary    dep
1   alex  12   2000     销售部
2   egon  22   3000     人事部
3   wen   22   5000     人事部

sql语句:
select dep,Count(*) from emp group by dep;

ORM:
emp.objects.values("dep").annotate(c=Count("id") #注意:annotate里面必须写个聚合函数,  不然没有意义,并且必须有个别名=,别名随便写,但是必须有,用哪个字段分组,values里面就写哪个字段,  annotate其实就是对分组结果的统计,统计你需要什么
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ② 多表分组

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
多表分组查询:

查询每一个部门名称以及对应的员工数

emp:

id  name age   salary   dep_id
1   alex  12   2000       1
2   egon  22   3000       2
3   wen   22   5000       2

dep

id   name
1    销售部
2    人事部

emp-dep:

id  name age   salary   dep_id   id   name
1   alex  12   2000       1      1    销售部
2   egon  22   3000       2      2    人事部
3   wen   22   5000       2      2    人事部

sql语句:
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id

ORM:
①  models.dep.objetcs.annotate(c=Count("emp_id")).values("name","c")    #以 dep表 为基础进行联表 通过'emp_id' 进行计数,并用 'c' 做关键字加入queryset类型字典      用values进行取值
②  models.Emp.objects.values('dep_id','name').annotate(a=Count(id))     # 联表之后  通过'dep_di'和'name' 为分组依据       取到 'dep_id' , 'name' ,  'count' 三个值

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
#单表:
    #查询每一个部门的id以及对应员工的平均薪水
    ret = models.Emp.objects.values('dep_id').annotate(s=Avg('salary'))
    #查询每个部门的id以及对对应的员工的最大年龄
    ret = models.Emp.objects.values('dep_id').annotate(a=Max('age'))
    #Emp表示表,values中的字段表示按照哪个字段group by,annotate里面是显示分组统计的是什么

#连表:
    # 查询每个部门的名称以及对应的员工个数和员工最大年龄
    ret = models.Emp.objects.values('dep__name').annotate(a=Count('id'),b=Max('age')) #注意,正向与反向的结果可能不同,如果反向查的时候,有的部门还没有员工,那么他的数据也会被统计出来,只不过值为0,但是正向查的话只能统计出来有员工的部门的相关数据,因为通过你是员工找部门,而不是通过部门找员工,结果集里面的数据个数不同,但是你想要的统计结果是一样的
    #<QuerySet [{'a': 1, 'dep__name': '销售部', 'b': 12}, {'a': 3, 'dep__name': '人事部', 'b': 22}]>
    #使用双下划线进行连表,然后按照部门名称进行分组,然后统计员工个数和最大年龄,最后结果里面显示的是部门名称、个数、最大年龄。

#注意:如果values里面有多个字段的情况:
ret = models.Emp.objects.values('dep__name','age').annotate(a=Count('id'),b=Max('age')) #是按照values里面的两个字段进行分组,两个字段同时相同才算是一组,看下面的sql语句
'''
    SELECT `app01_dep`.`name`, `app01_emp`.`age`, COUNT(`app01_emp`.`id`) AS `a`, MAX(`app01_emp`.`age`) AS `b` FROM `app01_emp` INNER JOIN `app01_dep` ON (`app01_emp`.`dep_id` = `app01_dep`.`id`) GROUP BY `app01_dep`.`name`, `app01_emp`.`age`;
'''
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ③补充:(联表操作时,相同数据默认保留第一个)

    在取最值是  适当运用 升降序, 避免出现错误

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  八.F查询  和 Q查询  

  1. F 查询(进行两个字段的比较)

  ①F() 来比较同一个 model 实例中两个不同字段的值。 

# 查询评论数大于收藏数的书籍

   from django.db.models import F
   Book.objects.filter(commentNum__lt=F('keepNum'))

  ② F() 对象和常数之间的加减乘除和取模的操作

# 查询评论数大于收藏数2倍的书籍
    Book.objects.filter(commentNum__lt=F('keepNum')*2)

  ③修改操作

比如将每一本书的价格提高30元:

Book.objects.all().update(price=F("price")+30) 

  2.Q查询  (or 方法)

  ①Q 对象可以使用&(与) 、|(或)、~(非) 操作符组合起来


 from django.db.models import Q
 
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

  ②组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象  

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
bookList=Book.objects.filter(Q(authors__name="yuan")   & ~Q(publishDate__year=2017)).values_list("title")

bookList=Book.objects.filter(Q(Q(authors__name="yuan")   & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以进行Q嵌套,多层Q嵌套等,其实工作中比较常用
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

九. ORM执行原生sql 语句

  1.raw()管理器方法用于原始的SQL查询

    得到一个model对象

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
①   举个例子:

class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)
  可以像下面这样执行原生SQL语句

>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
...     print(p)
②  raw()查询可以查询其他表的数据。

    举个例子:

ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher')
    for i in ret:
        print(i.id, i.hehe)
③  raw()方法自动将查询字段映射到模型字段。还可以通过translations参数    指定一个把查询的字段名和ORM对象实例的字段名互相对应的字典

d = {'tname': 'haha'}
    ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
    for i in ret:
        print(i.id, i.sname, i.haha)
④  原生SQL还可以使用参数,注意不要自己使用字符串格式化拼接SQL语句,防止SQL注入!

d = {'tname': 'haha'}
    ret = models.Student.objects.raw('select * from app02_teacher where id > %s',         translations=d, params=[1,])
    for i in ret:
        print(i.id, i.sname, i.haha)
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2.直接执行自定义SQL

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
from django.db import connection, connections
cursor = connection.cursor()  # cursor = connections['default'].cursor()
cursor.execute("SELECT * from auth_user")
ret = cursor.fetchall()      #直接得到一个元组
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

十.django  内外部调用

  1.外部py文件  调用django内部  

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") #manage.py文件中
    import django
    django.setup()

    from app01 import models  #引入也要写在上面三句之后

    books = models.Book.objects.all()
    print(books)
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2.内部 调用 外部

  ① 外部文件

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②内部调用

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

十一.自定义标签和过滤器

  1.自定义过滤器

    (最多接收2个参数,可以放到if ,for  等中)

  ① 在app01中新建 templatetags 文件夹

 day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②.在.html 文件中引入  调用

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2.自定义标签

  (可接收任意参数,不可放进if, for等循环语句中)

  (一)

      ①自定义标签

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②从view.py文件中传入数据

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ③调用自定义标签

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ④结果

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  3.自定义标签   inclusion_tag(类似于装饰器)

  ① 在test.html 中调用(参数为5)

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②自定义标签(并用 result.html 装饰)

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ③接收数据并进行装饰 返回给调用的test.html文件

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ④  在test.html中显示的结果

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  4.自定义标签 simple_tag

   (常用于返回标签)

  ①自定义(若没有mark_safe会显示字符串)

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②调用

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

十二. 锁

  1.锁的分类

  ①表级锁定

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
    表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是

实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁

一次会将整个表锁定,

所以可以很好的避免困扰我们的死锁问题。

    当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,

致使并大度大打折扣。使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)

  读锁 会阻塞写,不会阻塞读。

  写锁 则会把读写都阻塞
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
如何加表锁

显示加锁:
      共享读锁:lock table tableName read;
      独占写锁:lock table tableName write;

          同时加多锁:lock table t1 write,t2 read;
      批量解锁:unlock tables;
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
应用

第一种情况:全表更新。事务需要更新大部分或全部数据,且表又比较大。若使用行锁,会导致事务执行效率低,从而可能造成其他事务长时间锁等待和更多的锁冲突。

第二种情况:多表级联。事务涉及多个表,比较复杂的关联查询,很可能引起死锁,造成大量事务回滚。这种情况若能一次性锁定事务涉及的表,从而可以避免死锁、减少数据库因事务回滚带来的开销。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
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
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②行级锁定(InnoDB锁定模式)

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
  行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的

锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予

应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
    虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。

由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗

自然也就更大了。此外,行级锁定也最容易发生死锁。
    使用行级锁定的主要是InnoDB存储引擎。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
InnoDB的锁定模式实际上可以分为四种:共享锁(S),排他锁(X),              意向共享锁(IS)和意向排他锁(IX)

意向锁是InnoDB自动加的,不需用户干预

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

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

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
entries = Entry.objects.select_for_update().filter(author=request.user)  

#加互斥锁,由于mysql在查询时自动加的是共享锁,所以我们可以手动加上互斥锁。create、update、delete操作时,mysql自动加行级互斥锁

  ③页级锁定

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
  页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。

页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,

以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,

会发生死锁。
    在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据

所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。不过,随着锁定资源颗粒度的减小,

应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。
    使用页级锁定的主要是BerkeleyDB存储引擎。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ④

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
 总的来说,MySQL这3种锁的特性可大致归纳如下:
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,    发生锁冲突的概率最高,并发度最低;
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,    发生锁冲突的概率最低,并发度也最高; 
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;    锁定粒度界于表锁和行锁之间,并发度一般。
#适用:从锁的角度来说,表级锁更适合于以查询为主,    只有少量按索引条件更新数据的应用,如Web应用;    而行级锁则更适合于有大量按索引条件并发更新少量不同数据,    同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

十三.事务( 要么同时发生 , 要么同时不发生 )

    (出错了 回滚的是)

  1.全局开启

   ①在setting.py文件中进行配置(慎用)

day056-58 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',
            ......
  } #还可以配置其他数据库
}
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2.局部开启

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

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
from django.db import transaction

@transaction.atomic   #引入装饰器
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ②用法2:作为上下文管理器来使用,

    其实就是设置事务的保存点

      一旦把atomic代码块放到try/except中,完整性错误就会被自然的处理掉了

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
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()
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
过程:  
1.进入最外层atomic代码块时开启一个事务;
2.进入内部atomic代码块时创建保存点;  
3.退出内部atomic时释放或回滚事务;注意如果有嵌套,    内层的事务也是不会提交的,可以释放(正常结束)或者回滚
4.退出最外层atomic代码块时提交或者回滚事务;
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  ③.设置保存点

  1)在命令框时

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
1、查看user表中的数据

mysql> select * from user;
+-----+----------+-----+------+
| mid | name | scx | word |
+-----+----------+-----+------+
| 1 | zhangsan | 0 | NULL |
| 2 | wangwu    | 1 | NULL |
+-----+----------+-----+------+
rows in set (0.05 sec)
2、mysql事务开始

mysql> BEGIN; -- 或者start transaction;
Query OK, 0 rows affected (0.00 sec)
3、向表user中插入2条数据

mysql> INSERT INTO user VALUES ('3','one','0','');
Query OK, 1 row affected (0.08 sec)
mysql> INSERT INTO user VALUES ('4,'two','0','');
Query OK, 1 row affected (0.00 sec)
mysql> select * from user;
+-----+----------+-----+------+
| mid | name | scx | word |
+-----+----------+-----+------+
| 1 | zhangsan | 0 | NULL |
| 2 | wangwu    | 1 | NULL |
| 3 | one            | 0 | |
| 4 | two             | 0 | |
+-----+----------+-----+------+
rows in set (0.00 sec)
4、指定保存点,保存点名为test

mysql> SAVEPOINT test;
Query OK, 0 rows affected (0.00 sec)
5、向表user中插入第3条数据

mysql> INSERT INTO user VALUES ('5','three','0','');
Query OK, 1 row affected (0.00 sec)
mysql> select * from user;
+-----+----------+-----+------+
| mid | name | scx | word |
+-----+----------+-----+------+
| 1 | zhangsan | 0 | NULL |
| 2 | wangwu | 1 | NULL |
| 3 | one | 0 | |
| 4 | two | 0 | |
| 5 | three | 0 | |
+-----+----------+-----+------+
rows in set (0.02 sec)
6、回滚到保存点test

mysql> ROLLBACK TO SAVEPOINT test;
Query OK, 0 rows affected (0.31 sec)
mysql> select * from user;
+-----+----------+-----+------+
| mid | name | scx | word |
+-----+----------+-----+------+
| 1 | zhangsan | 0 | NULL |
| 2 | wangwu    | 1 | NULL |
| 3 | one            | 0 | |
| 4 | two            | 0 | |
+-----+----------+-----+------+
rows in set (0.00 sec)
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

  2)   django 中 用  transaction 的方法

day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
@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,那么就需要自己手动进行提交。
day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁
上一篇:ACM/ICPC 之 机器调度-匈牙利算法解最小点覆盖集(DFS)(POJ1325)


下一篇:HTB-靶机-Waldo