django之模型层 各种查询 数据库查询优化相关 事务使用

相关知识盘点:查看orm内部sql语句的方法
    1.看是否是queryset 对象,是的话,可直接.query查看SQL语句;
    2.在django配置文件中,配置相关参数,orm查询时自动打印sql语句;
        LOGGING = {
            'version': 1,
            'disable_existing_loggers': False,
            'handlers': {
                'console':{
                    'level':'DEBUG',
                    'class':'logging.StreamHandler',
                },
            },
            'loggers': {
                'django.db.backends': {
                    'handlers': ['console'],
                    'propagate': True,
                    'level':'DEBUG',
                },
            }
        }
        
案例相关:
    from django.db import models
    class Book(models.Model):
        title = models.CharField(max_length=255)
        price = models.DecimalField(max_digits=8,decimal_places=2)
    
DateFild的两个关键性参数:
  1.autu_now:每次操作数据都会自动刷新当前操作时间
  2.auto_now_add:只在创建数据时记录创建时间,之后的操作不影响该字段
        publish_date = models.DateField(auto_now_add=True)
        # 库存数
        kucun = models.IntegerField(null=True)
        # 卖出数
        maichu = models.IntegerField(null=True)
        publish = models.ForeignKey(to='Publish')  # 默认是跟publish的主键字段做的一对多外键关联
        authors = models.ManyToManyField(to='Author')
        # 虚拟字段      1.自动创建第三张表    2.帮助orm跨表查询

    class Publish(models.Model):
        name = models.CharField(max_length=32)
        addr = models.CharField(max_length=32)
        # email = models.EmailField()  # 就是varchar(254)

    class Author(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField()
        author_detail = models.OneToOneField(to='AuthorDetail')

    class AuthorDetail(models.Model):
        phone = models.BigIntegerField()
        addr = models.CharField(max_length=64)
        
1.查询的方法:
    1.all(): 查询所有结果
    
    2.filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
    
    3.get(**kwargs): 返回数据对象,结果只有一个,没匹配到报错
    
    4.exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
      eg:
         print(models.Book.objects.exclude(pk=1))  #只要pk不是1的数据全部查询出来

    5.order_by(*field): 对查询结果排序;默认是升序,加-就是降序
      eg:
        print(models.Book.objects.order_by('price'))   # 默认是升序
        print(models.Book.objects.order_by('-price'))  # 加负号就是降序

    6.reverse(): 对查询结果反向排序 
      eg:
        print(models.Book.objects.order_by('price').reverse())
        
        注:前面要先有排序才能反向

    7.count(): 返回数据库中匹配查询(QuerySet)
      eg: 
        print(models.Book.objects.count()) #对查询出来的结果进行一个计数

    8.first(): 返回第一条记录 
    
    9.last(): 返回最后一条记录

    10.exists():存在返回True,否则返回False
      eg:
        print(models.Book.objects.filter(pk=1000).exists())

    11.values(*field): 返回一个ValueQuerySet对象,运行后得到的并不是一系列
      eg:
        print(models.Book.objects.values('title','price'))  # 得到的结果是列表套字典

    12.values_list(*field): 它与values()类似
      eg:
        print(models.Book.objects.values_list('title','price'))  # 得到的结果是列表套元组

    13.distinct(): 从返回结果中剔除重复纪录
      eg:
        print(models.Book.objects.values('title','price','create_time').distinct())
        
        注:去重的前提是 一定要有完全重复的数据 才能去重

2.双下滑查询:
    1.__gt: 大于
        eg:查询价格大于200的书籍
        res = models.Book.objects.filter(price__gt=200)
    2.__lt: 小于
        eg:查询价格小于200的书籍
        res = models.Book.objects.filter(price__lt=200)
    3.__gte:大于等于
        eg:查询价格大于等于200.22的书籍
        res = models.Book.objects.filter(price__gte=200.22)
    4.__lte:小于等于
        eg:查询价格小于等于200.22的书籍
        res = models.Book.objects.filter(price__lte=200.22)
    5.__in:或者
        eg:查询价格要么是200,要么是300,要么是666.66
        res = models.Book.objects.filter(price__in=[200,300,666.66])
    6.__range:区间 左右都包括
        eg:查询价格在200到800之间的
        res = models.Book.objects.filter(price__range=(200,800))
    7.__contains:模糊匹配
        eg:查询书籍名字中包含p的
        res = models.Book.objects.filter(title__contains='p')  # 仅仅只能拿小写p
        res = models.Book.objects.filter(title__icontains='p')  # 忽略大小写
    8.__startswith:以...开头
        eg:查询书籍是以三开头的
        res = models.Book.objects.filter(title__startswith='')
    9.__endswith:以...结尾
        eg:查询书籍是以三结尾的
        res = models.Book.objects.filter(title__endswith='')
    10.__year:查询年相关的
        eg:查询出版日期是2017的年(******)
        res = models.Book.objects.filter(create_time__year='2017')
            
    

3.多表查询:
    一对多的字段的增删改查:
        增:
            publish_id传数字:
                models.Book.objects.create(title='三国演义',price=189.99,publish_id=1)
            
            publish直接传出版社对象:
                publish_obj = models.Publish.objects.filter(pk=2).first()
                models.Book.objects.create(title='红楼梦',price=999.99,publish=publish_obj)

        改:
            传数字的:
                models.Book.objects.filter(pk=1).update(publish_id=3)
            传对象的:
                publish_obj = models.Publish.objects.filter(pk=2).first()
                models.Book.objects.filter(pk=1).update(publish=publish_obj)

        删:
            models.Publish.objects.filter(pk=2).delete()  # 默认都是级联更新 级联删除


    多对多字段的增删改查:
        增(add):既可以传数字也可以传对象并且支持一次性传多个,逗号隔开即可
            1.要给主键为1的书籍添加两个作者
                传数字:
                    book_obj = models.Book.objects.filter(pk=1).first()
                    book_obj.authors.add(1)
                    book_obj.authors.add(2,3)
                传对象:
                    author_obj = models.Author.objects.filter(pk=1).first()
                    author_obj1 = models.Author.objects.filter(pk=2).first()
                    author_obj2 = models.Author.objects.filter(pk=3).first()
                    book_obj.authors.add(author_obj)
                    book_obj.authors.add(author_obj1,author_obj2)
                
        改(set):
            1.将主键为1的书籍对象 作者修改为2,3
                传数字:
                    book_obj = models.Book.objects.filter(pk=1).first()
                    book_obj.authors.set([2,])
                    book_obj.authors.set([2,3])
                传对象:
                    author_obj = models.Author.objects.filter(pk=1).first()
                    author_obj1 = models.Author.objects.filter(pk=2).first()
                    author_obj2 = models.Author.objects.filter(pk=3).first()
                    book_obj.authors.set([author_obj,])
                    book_obj.authors.set([author_obj, author_obj1, author_obj2])
                
                注:1.set中要传可迭代对象,可迭代对象中 可以是多个数字组合也可以是多个对象组合;
                   2.数字与对象不要混着用!!!

        删(remove|clear):remove支持传数字,对象,并且可以传多个; clear不需要传任何参数
            传数字:
                book_obj = models.Book.objects.filter(pk=1).first()
                book_obj.authors.remove(3)
                book_obj.authors.remove(1,2)
            传对象:    
                author_obj = models.Author.objects.filter(pk=1).first()
                author_obj1 = models.Author.objects.filter(pk=2).first()
                author_obj2 = models.Author.objects.filter(pk=3).first()
                book_obj.authors.remove(author_obj)
                book_obj.authors.remove(author_obj1,author_obj2)
            什么都不传:将xx跟xx的关系全部清空
                book_obj = models.Book.objects.filter(pk=1).first()
                book_obj.authors.clear()  # 清空当前书籍与作者的所有关系
        
        注:    对象点击多对多虚拟字段 会直接跨到多对多的第三张表
                
4.跨表查询:
    正向与反向概念:
        1.一对一
            正向:author---关联字段在author表里--->authordetail        按字段
            反向:authordetail---关联字段在author表里--->author        按表名小写                          
        2.一对多
            正向:book---关联字段在book表里--->publish                 按字段
            反向:publish---关联字段在book表里--->book                 按表名小写_set.all() 因为一个出版社对应着多个图书            
        3.多对多
            正向:book---关联字段在book表里--->author                  按字段
            反向:author---关联字段在book表里--->book                  按表名小写_set.all() 因为一个作者对应着多个图书
        小结:(*****)
            正向查询按外键字段查,反向查询按表名小写
            
    子查询:将一张表的查询结果当做另外一个查询语句的条件
        1.查询书籍id是1 的出版社名称
            book_obj = models.Book.objects.filter(pk=1).first()
            print(book_obj.publish.name)
        
        2.查询书籍id是2 的作者姓名
            book_obj = models.Book.objects.filter(pk=2).first()
            print(book_obj.authors)  # app01.Author.None
            print(book_obj.authors.all())
            res = book_obj.authors.all()
            for r in res:
                print(r.name)
                
        3.查询作者是jason的家庭住址
            author_obj = models.Author.objects.filter(name='jason').first()
            print(author_obj.author_detail.addr)

        4.查询出版社是东方出版社出版的书籍
            publish_obj = models.Publish.objects.filter(name='东方出版社').first()
            # print(publish_obj.book_set)  # app01.Book.None
            print(publish_obj.book_set.all())

        5.查询作者是jason的写过的所有的书籍
            author_obj = models.Author.objects.filter(name='jason').first()
            print(author_obj.book_set)  # app01.Book.None
            print(author_obj.book_set.all())

        6.查询电话号码是130的作者姓名
            author_detail_obj = models.AuthorDetail.objects.filter(phone=130).first()
            print(author_detail_obj.author.name)
            print(author_detail_obj.author.age)
                
        7.查询书籍id为1 的作者的电话号码
            book_obj = models.Book.objects.filter(pk=1).first()
            author_list = book_obj.authors.all()
            for author_obj in author_list:
                print(author_obj.author_detail.phone)
                    
        小结:
            当反向查询的结果有多个时,要在后面加上_set;
    
    连表操作(基于__):
        1.查询jason作者的手机号
            正向:    
                res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')
                print(res)
            反向:
                res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
                print(res1)

        2.查询jason这个作者的年龄和手机号
            正向
                res = models.Author.objects.filter(name='jason').values('age','author_detail__phone')
                print(res)
            反向
                res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
                print(res1)

        3.查询手机号是130的作者年龄
            正向
                res = models.AuthorDetail.objects.filter(phone=130).values('author__age')
                print(res)
            反向:
                res1 = models.Author.objects.filter(author_detail__phone=130).values('age')
                print(res1)

        4.查询书籍id是1 的作者的电话号码
            res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
            print(res)
        
        5.查询出版社为北方出版社的所有图书的名字和价格
            res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price')
            print(res)

        6.查询北方出版社出版的价格大于19的书
            res = models.Book.objects.filter(price__gt=19,publish__name='北方出版社').values('title','publish__name')
            print(res)

        注:只要表里面有外键字段,你就可以无限制跨多张表    
            eg:外键字段1__外键字段2...__外键字段__普通字段n
            
    聚合查询(aggregate):最大,最小,个数,平均,总和
        先导入 from django.db.models import Max,Min,Count,Avg,Sum...
            res = models.Book.objects.aggregate(Sum('price'))
            res1 = models.Book.objects.aggregate(Avg('price'))
            res2 = models.Book.objects.aggregate(Count('price'))
            res3 = models.Book.objects.aggregate(Max('price'))
            res4 = models.Book.objects.aggregate(Min('price'))
            res5 = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Avg('price'),Sum('price'))
        
        注: aggregate(后面可跟多个函数)
        
        
    分组查询(annotate):models.那个表名就以那个分组
        from django.db.models import Max, Min, Count, Avg, Sum
        1.统计每一本书的作者个数
            res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title')
            print(res)

        2.统计出每个出版社卖的最便宜的书的价格
            res = models.Publish.objects.annotate(mmp = Min('book__price')).values('name','mmp')
            print(res)

        3.统计不止一个作者的图书
            res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
            print(res)
                    
        4.查询各个作者出的书的总价格
            res = models.Author.objects.annotate(sp=Sum('book__price')).values('name','sp')
            print(res)

    F查询:本质就是从数据库中获取某个字段的值
        from django.db.models import F
        1.查询卖出数量
            res = models.Book.objects.filter(kucun__gt=F('maichu'))
            print(res)

        2.将书籍库存数全部增加1000
            models.Book.objects.update(kucun=F('kucun')+1000)

        3.把所有书名后面加上'新款'
            要先导入:
            from django.db.models.functions import Concat
            from django.db.models import Value

            ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款')))
            models.Book.objects.update(title = F('title')+'新款')  # 不能这么写
    
    Q查询:默认and,可人为改为 or , not
        from django.db.models import Q
        1.查询书籍名称是三国演义或者价格是444.44
          and:
            res = models.Book.objects.filter(title='三国演义',price=444.44)  # filter只支持and关系
            res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444))  # 如果用逗号 那么还是and关系
          
          or:
            res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444))
          
          not:
            res3 = models.Book.objects.filter(~Q(title='三国演义')|Q(price=444))
    
    Q高级用法(了解):
        q = Q()
        q.connector = 'or'  # 修改查询条件的关系   默认是and
        q.children.append(('title__contains','三国演义'))  # 往列表中添加筛选条件
        q.children.append(('price__gt',444))  # 往列表中添加筛选条件
        res = models.Book.objects.filter(q)  # filter支持你直接传q对象  但是默认还是and关系
        print(res)
数据库查询优化相关
    orm内所有的语句操作都是惰性查询:只会在你真正需要数据的时候才会走数据库,这样有利于减轻数据库压力
    only与defer
    
        only:会将only()内的字段一次性的查出来返封装进一个对象中,
            你查only内的数据,不管取值多少次,也只会走一次数据库 ,
            你查的不是only内的数据,不会报错也会返回数据,但是会频繁的走数据库
        defer:会将不是defer()内的所有字段信息全查出来封装进对象中, 
            你查询()内的字段会频繁的走数据库,对手库的压力比较大,反之只走一次库
        
    select_related与prefetch_related
        select_related:会将括号内外键字段所关联的那张表直接全部拿过来(可以一次性拿多张表)跟当前表拼接操作
            从而降低你跨表查询 数据库的压力
          注:select_related括号只能放外键字段(一对一和一对多)
          
        prefetch_related:不主动连表操作(但是内部给你的感觉像是连表操作了),会将book表中的publish_id全部拿
            来,再去publish表中将id对应的所有的数据取出
            
        区别:    select_related需要连表,但只走一次数据库;连表会花时间
                prefech_related不用连表,括号内有几个外键字段 就会走几次数据库查询操作
    
    
事务(acid):
    事务使用:
        from django.db import transaction
        
            with transaction.atomic():
                """数据库操作
                在该代码块中书写的操作 同属于一个事务
                """
                models.Book.objects.create()
                models.Publish.objects.create()
                # 添加书籍和出版社 就是同一个事务 要么一起成功要么一起失败
            print('出了 代码块 事务就结束')        

 

posted @ 2019-09-19 20:04  54菜鸟  阅读(371)  评论(0编辑  收藏  举报