07 模型层 orm相关查询 F查询Q查询 django开启事务

一、Django终端打印SQL语句

如果你想知道你对数据库进行操作时,Django内部到底是怎么执行它的sql语句时可以加下面的配置来查看

在Django项目的settings.py文件中,在最后复制粘贴如下代码:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
查看内部sql语句配置

补充:

除了配置外,还可以通过一点.query即可查看查询语句,具体操作如下:

 

 

二、测试环境

我们测试的时候不想一直通过网络请求,可以用app里面的test文件也可以自己新建test测试文件来直接测试,配置如下:

 

 

 三、单表查询

1.单表增删改查

    #
    # 方式1: create(这个有返回值,返回对象本身
    # book_obj  = models.Book.objects.create(title='三国',price=19.99,create_time='2019-11-11')
    # print(book_obj.title)
    # 方式2:对象点save()方法
    # from datetime import datetime
    # ctime = datetime.now()
    # book_obj = models.Book(title='金色梅',price=96.66,create_time=ctime)
    # book_obj.save()

    #
    # print(models.Book.objects.all())
    # print(models.Book.objects.get(pk=1)) #不存在会立刻报错,测试使用
    # print(models.Book.objects.filter(pk=2))
    """
    pk会自动查找到当前数据的主键字段,无论你是id、tid、sid都能找到
    """
    
    #
    # 1.update
    # models.Book.objects.filter(pk=1).update(title='三国演义')  #批量更新,指哪打哪
    # 2.对象.save()
    # book_obj = models.Book.objects.get(pk=1)
    # book_obj.price = 666.66
    # book_obj.save() #这个效率极低,会将此对象所有属性从头到尾更新一遍

    # 删除  delete()
    # models.Book.objects.filter(pk=2).delete()
   #

 

2.必会13种方法

 

   # < 1 > all(): 查询所有结果

    # < 2 > filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
    # < 3 > get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。(源码就去搂一眼~诠释为何只能是一个对象)
    # < 4 > exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
    # print(models.Book.objects.exclude(pk=1))  # 只要pk不是1的数据全部查询出来

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

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

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

    # 的对象数量。
    # < 8 > first(): 返回第一条记录
    # print(models.Book.objects.filter(pk=1).first())
    # < 9 > last(): 返回最后一条记录
    # print(models.Book.objects.all().last())

    # < 10 > exists(): 如果QuerySet包含数据,就返回True,否则返回False,没毛用
    # print(models.Book.objects.filter(pk=1000))
    # print(models.Book.objects.filter(pk=1000).exists())


    # < 11 > values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
    # model的实例化对象,而是一个可迭代的字典序列
    # print(models.Book.objects.values('title','price'))  # 得到的结果是列表套字典

    # < 12 > values_list(*field): 它与values()非常相似,他返回列表套元祖,values返回列表套字典
    # print(models.Book.objects.values_list('title','price'))  # 得到的结果是列表套元组 

  # < 13 > distinct(): 从返回结果中剔除重复纪录 #去重的前提是 一定要有完全重复的数据 才能去重 # print(models.Book.objects.filter(title='三国演义').distinct()) # print(models.Book.objects.values('title','price','create_time').distinct())

3.神奇的双下方法

            价格大于 小于 大于等于  小于等于   在什么之间  或好几个选择
            price__gt
            price__lt
            price__gte
            price__lte
            price__in
            price__range
        
            书籍名称中包含
                title__contains='p'
                title__icontains='p'
            
            创建的年份
                create_time__year

 

"""神奇的双下滑查询"""
        # 查询价格大于200的书籍
        # res = models.Book.objects.filter(price__gt=200)
        # 查询价格小于200的书籍
        # res = models.Book.objects.filter(price__lt=200)


        # 查询价格大于等于200.22的书籍
        # res = models.Book.objects.filter(price__gte=200.22)
        # 查询价格小于等于200.22的书籍
        # res = models.Book.objects.filter(price__lte=200.22)

        # 查询价格要么是200,要么是300,要么是666.66
        # res = models.Book.objects.filter(price__in=[200,300,666.66])
        # 查询价格在200到800之间的
        # res = models.Book.objects.filter(price__range=(200,800))  # 两边都包含

        # 查询书籍名字中包含p的 (mysql中才有效,自带小库不行)
        """原生sql语句 模糊匹配
            like 关键字
                % 匹配任意
                _ 匹配一个
        """
        # res = models.Book.objects.filter(title__contains='p')  # 仅仅只能拿小写p
        # res = models.Book.objects.filter(title__icontains='p')  # 忽略大小写

        # 查询书籍是以三开头的
        # res = models.Book.objects.filter(title__startswith='三')
        # res1 = models.Book.objects.filter(title__endswith='p')
    
        # 查询出版日期是2017的年(******)
        #res = models.Book.objects.filter(create_time__year='2017')

四、多表操作(图书管理系统)

图书管理系统表创建
    一对多:ForeignKey
    一对一:OnoToOneField  可以用ForeignKey代替,ForeignKey(unique=True)
        上面两个关键字所创建出来的字段会自动加上_id后缀
        
    多对多:ManyToManyFiled
    该字段并不会真正的在表中展示出来 它仅仅是一个虚拟字段
        1.告诉orm自动创建第三种表
        2.帮助orm跨表查询

models.py

from django.db import models

# Create your models here.
class Book(models.Model):
    title = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True) #auto_now_add只会在创建的时候创建日期,auto_now会在每一次修改数据时创建新日期

    # 库存数
    kucun = models.IntegerField(null=True) #表已经创建好后新增字段必须设置default=xxx或者null=True
    # 卖出数
    maichu = models.IntegerField(null=True)

    publish = models.ForeignKey(to='Publish')  # 默认是跟publish的主键字段做的一对多外键关联
    authors = models.ManyToManyField(to='Author')
    # 虚拟字段      1.自动创建第三张表    2.帮助orm跨表查询

    def __str__(self):
        return self.title

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

    def __str__(self):
        return self.name

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

    def __str__(self):
        return self.name
class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=64)
    """
    models.py中的模型类__str__方法 必须返回一个字符串形式数据!!!
    
    """
    def __str__(self):
        return self.addr

1.外键字段的增删改查

# 一对多的字段的增删改查

    #
    # 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()  # 默认都是级联更新 级联删除

# 多对多字段的增删改查

在多对多表关系并且这一张多对多的关系表是由Django自动帮你建的情况下,下面的方法才可使用

    #
    # 要给主键为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)
    """
    add()
    是给书籍添加作者  括号内既可以传数字也可以传对象
    并且支持一次性传多个  逗号隔开即可
    """

    #
    # 将主键为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])
    """
    set()括号内 需要传一个可迭代对象 
    可迭代对象中 可以是多个数字组合
    也可以是多个对象组合
    但是不要混着用!!!
    """

    #
    # 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)
    """
    remove()括号内既可以传数字 也可以传对象 
    并且支持传对个 逗号隔开即可
    """

    # 将某本书跟所有作者的关系全部清空
    # book_obj = models.Book.objects.filter(pk=1).first()
    # book_obj.authors.clear()  # 清空当前书籍与所有作者的所有关系

    """
    add()
    set()
    remove()
    上面三个都支持传数字 或者对象 并且可以传多个 但是set需要传可迭代对象
    
    clear()
    clear括号内不需要传任何参数
    """

2.ORM跨表查询(******)

2.1 正向与反向的概念


#
一对一 # 正向:author---关联字段在author表里--->authordetail 按字段 # 反向:authordetail---关联字段在author表里--->author 按表名小写 # 一对多 # 正向:book---关联字段在book表里--->publish 按字段 # 反向:publish---关联字段在book表里--->book 按表名小写_set.all() 因为一个出版社对应着多个图书 # 多对多 # 正向:book---关联字段在book表里--->author 按字段 # 反向:author---关联字段在book表里--->book 按表名小写_set.all() 因为一个作者对应着多个图书

总结:如果手上有关联字段就是正向,如果没有就是反向
    正向查询按外键字段
    反向查询按表名小写

2.2基于对象跨表查询

    """基于对象的跨表查询(子查询:将一张表的查询结果当做另外一个查询语句的条件)"""
    """
    强调:在书写orm语句的时候 跟写sql语句一样 
    不要尝试着 一次性写完  应该做到写一点看一点再一点
    """
    # 1.查询书籍id是1 的出版社名称
    # book_obj = models.Book.objects.filter(pk=1).first()
    # print(book_obj.publish.name)
    # print(book_obj.publish.addr)

    # 2.查询书籍id是2 的作者姓名
    # book_obj = models.Book.objects.filter(pk=2).first()
    # print(book_obj.authors)  # app01.Author.None  有多个对象时必须加all
    # 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()) #反向多个时,必须加_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) #反向一个不需要加set
    """
   当你查询结果是多个时需要加all
   否则直接外键字段即可 当你反向查询的结果是多个的时候 还需要加_set 否则直接表名小写即可
""" # 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)

2.3基于双下划线的查询

 """基于双下划线的跨表查询(连表操作)
        left join
        inner join
        right join
        union
    """
    # 1.查询jason作者的手机号
   # res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr') # 正向查 author_detail是外键字段 # print(res) # res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone') #反向查 author是表名字小写 # print(res1) # 查询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) # 查询手机号是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) # 查询书籍id是1 的作者的电话号码 # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone') #authors带s的是字段名 # res1 = models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段') # print(res) """只要表里面有外键字段 你就可以无限制跨多张表""" # 1.查询出版社为北方出版社的所有图书的名字和价格 # res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price') # print(res) # 2.查询北方出版社出版的价格大于19的书 # res = models.Book.objects.filter(price__gt=19,publish__name='北方出版社').values('title') # print(res)

3.聚合查询(aggregate)

需要导入 Sum,Max,Min,Avg,Count
from django.db.models import Sum,Max,Min,Avg,Count
# 1.统计所有书的总价格
    # # 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'))

4.分组查询(annotate)

    from django.db.models import Max, Min, Count, Avg, Sum
    # 统计每一本书的作者个数
    # res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title')
    # print(res)

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

    # 统计不止一个作者的图书
    # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
    # print(res)

    # 查询各个作者出的书的总价格
    # res = models.Author.objects.annotate(sp=Sum('book__price')).values('name','sp')
    # print(res)
"""
    只要是queryset对象 就可以无限制的调用queryset对象的方法!!!
    最最常用的就是对一个已经filter过滤完的数据 再进行更细化的筛选
    
"""

5.F查询

需要导入F模块

#F查询的本质就是从数据库中获取某个字段的值

    """之前查询等号后面的条件都是我们认为输入的
        现在变成了需要从数据库中获取数据放在等号后面
    """
    from django.db.models import F
    
  #查询库存数大于卖出数的书籍
  # res = models.Book.objects.filter(kucun__gt=F('maichu')) # print(res) # 将书籍库存数全部增加1000 # models.Book.objects.update(kucun=F('kucun')+1000) # 把所有书名后面加上'新款' (了解即可)    #先导入下面两个模块 # 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')+'新款') # 不能这么写,会报错还会破坏数据库原有名字

6.Q查询

或 与 非就用Q

from django.db.models import Q
    # 查询书籍名称是三国演义或者价格是444.44
    # res = models.Book.objects.filter(title='三国演义',price=444.44)  # filter只支持and关系,不支持或关系
    # res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444))  # 如果用逗号 那么还是and关系
    
    # res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444)) #或

    # 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)

 7.事务

 事务的定义:将多个sql语句操作变成原子性操作,要么同时成功,有一个失败则里面回滚到原来的状态,保证数据的完整性和一致性(NoSQL数据库对于事务则是部分支持)

django使用事务:

from django.db import transaction

with transaction.atomic():
    """数据库操作
    在该代码块中书写的操作 同属于一个事务
    """
    models.Book.objects.create()
    models.Publish.objects.create()
    # 添加书籍和出版社 就是同一个事务 要么一起成功要么一起失败
print('出了 代码块 事务就结束')

 

posted @ 2019-09-22 22:00  www.pu  Views(352)  Comments(0Edit  收藏  举报