11_06、ORM多表操作

一、测试环境

1、什么是测试环境

在Python脚本中调用Django环境

测试环境是指不需要启动整个Django框架项目,而是在应用中的test.py文件中通过一系列的导入

实现整个项目可以在一个py文件中的简单启动方式,通过操作这个test.py文件,方便我们对整个Django项目的测试。

2、测试环境的准备

复制代码
'''测试环境准备'''
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm操作数据库.settings")
    import django

    django.setup()
    '''测试环境准备完毕'''
    '''若想测试环境生效,新增加的代码必须写在下面'''

# 举例
my_rest = 1 * 2
print(my_rest)  # 2
复制代码

二、ORM的其他查询方法

1、查询表纪录

查询API

 

复制代码
<1> all():                  查询所有结果
  
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象
  
<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
  
<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象
 
<5> order_by(*field):       对查询结果排序('-id')
  
<6> reverse():              对查询结果反向排序
  
<8> count():                返回数据库中匹配查询(QuerySet)的对象数量。
  
<9> first():                返回第一条记录
  
<10> last():                返回最后一条记录
  
<11> exists():              如果QuerySet包含数据,就返回True,否则返回False
 
<12> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
                            model的实例化对象,而是一个可迭代的字典序列
<13> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
 
<14> distinct():            从返回结果中剔除重复纪录
复制代码

2、基于双下划线的模糊查询 

复制代码
Book.objects.filter(price__in=[100,200,300])
Book.objects.filter(price__gt=100)
Book.objects.filter(price__lt=100)
Book.objects.filter(price__gte=100)
Book.objects.filter(price__lte=100)
Book.objects.filter(price__range=[100,200])
Book.objects.filter(title__contains="python")
Book.objects.filter(title__icontains="python")
Book.objects.filter(title__startswith="py")
Book.objects.filter(pub_date__year=2012)
复制代码

3、外键字段的增删改查

我们依然以之前的4张表为例,探究ORM对外键字段的增删改查操作

复制代码
# models.py文件

'''
创建外键关系''' '''创建外键关系表的时候,先创建基础字段,最后再添加外键字段''' # 1. 图书表 class Book(models.Model): title = models.CharField(max_length=32, verbose_name='图书标题') # price int # price = models.IntegerField() # 创建出来int类型 # price decimal(8, 2) ''' max_digits=None, 代表存储的总长度 decimal_places=None, 代表的是存储的小数位 ''' price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格') # 可以存小数 ########################## 图书表——出版社表, 一对多外键关系 ########################## # publish_id = models.ForeignKey(to='Publish', to_field='id') ''' 如果关联的字段是主键,那么to_field可以省略不写,默认关联的就是主键 如果你关联的不是主键字段,那么这个参数就不能省略,必须显式指定 ''' # publish_id = models.ForeignKey(to='Publish') ''' 当我们创建一对多关系的时候,字段的后缀_id就不要再自己添加了 而是,自动帮我们添加_id字段 ''' publish = models.ForeignKey(to='Publish', null=True) # null=True字段记录可为空,若不设置,无法迁移 ########################## 图书表——作者表,多对多外键关系 ########################## ''' authors是一个虚拟字段,不会在Book表中创建出来authors字段,而是,会自定帮助我们创建出来第三张表 此时的第三张表就是图书与作者之间的关系表 ''' authors = models.ManyToManyField(to='Author', ) # 2. 出版社表 class Publish(models.Model): ''' verbose_name:对当前字段进行解释,每个数据类型都有这个参数 ''' title = models.CharField(max_length=64, verbose_name='出版社标题') addr = models.CharField(max_length=32) # 3. 作者表 class Author(models.Model): name = models.CharField(max_length=32) ############################ 作者表——作者详情表,一对一外键关系 ########################## ''' 当我们创建一对一关系的时候,字段的后缀id就不要再加了,而是默认添加_id的结尾 ''' author_detail = models.OneToOneField(to='AuthorDetail', null=True) # null=True字段记录可为空,若不设置,无法迁移 # 4. 作者详情表 class AuthorDetail(models.Model): phone = models.CharField(max_length=64) wx = models.CharField(max_length=32)
复制代码

一对多,一对一,多对多

复制代码
   # test.py文件

'''外键字段的增删改查''' '''针对一对一,一对多的处理''' '''增加''' # 增加一本书 # 方式一:利用publish_id # 此处publish_id是外键字段,利用外键字段增加一本书 # res = models.Book.objects.create(title='三国', price=100, publish_id=1) # 方式二:利用publish=对象 # # 此处publish是一个对象,利用对象增加一本书 # publish_obj = models.Book.objects.filter(pk=2).first() # res = models.Book.objects.create(title='西游', price=200, publish=publish_obj) '''修改''' # 方式一: # 修改主键为1的外键字段为2 # res = models.Book.objects.filter(pk=1).update(publish_id=2) # 方式二: # 修改主键为2的外键字段为3 # publish_obj = models.Book.objects.filter(pk=3).first() # 生成外键为3的对象 # models.Book.objects.filter(pk=2).update(publish=publish_obj) # 找到主键为2的对象 '''针对多对多的处理''' '''增加add''' # 给主键为1的图书增加两个作者 # book_obj = models.Book.objects.filter(pk=1).first() # 先找到主键为1的图书 # 补充: # print(book_obj.authors) # authors是虚拟字段,连接图书和作者两张表(多对多) # 打印结果为app01.Author.None,它代表着我们已经处在这张虚拟表中了 # print(book_obj.authors.all()) # 打印结果是一个作者列表 # 方式一:根据主键添加 # book_obj.authors.add(1, 2) # ORM提供的方法,括号里写的是作者的id # 方式二:根据对象添加 # 根据主键创建两个作者对象 # author_obj1 = models.Author.objects.filter(pk=1).first() # author_obj2 = models.Author.objects.filter(pk=2).first() # 为主键为1的图书增加两个作者 # book_obj.authors.add(author_obj1, author_obj2) '''改set''' # 把pk=1的书籍的作者改为作者id=1的 # book_obj = models.Book.objects.filter(pk=1).first() # book_obj.authors.set([1]) # set括号内必须是一个可迭代对象,把pk=1的书籍的作者改为作者id=1的 # book_obj.authors.set([1, 2]) # 把pk=1的书籍的作者改为作者id=1和id=2的(多对多:两人合写一本书) # set的底层原理是:如果原来就有,则不变;多了删除;少了增加。 '''删除remove''' # 删除主键为1的图书 book_obj = models.Book.objects.filter(pk=1).first() book_obj.authors.remove(1) # 删除1个 book_obj.authors.remove(2, 3) # 删除多个 '''清空clear''' book_obj.authors.clear() # 把book中id=1的全部删除
复制代码

三、终端中查看SQL语句的执行情况

在settings.py文件中配置下面一段代码

配置完成后,在test.py文件中运行py文件,可以把用ORM操作数据库的语句,通过print()自动转换为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',
        },
    }
}
复制代码

四、正反向的概念

1、什么是正反向查询

在多表查询的时候,存在正反向查询的概念

正向查询:A和B,外键字段在A,A查B,就是正向查询

反向查询:A和B,外键字段在B,A查B,就是反向查询

简而言之:

    外键字段在我手上,我查你就是正向
    外键字段不在我手上,我查你就反向

举例:
    1. book     >>>  publish    >>>  正向
    2. publish  >>>  book       >>>  反向
    3. author   >>>  book       >>>  反向
    4. book     >>>  author     >>>  正向

2、正反向查询的口诀

1. 正向查询按照字段(外键字段)
2. 反向查询按照表名小写或者表名小写_set.all()

注意:

表名小写:只有一种结果

表名小写_set.all():结果有不止一个的可能

3、应用方向

基于对象的跨表查询和基于双下划线的跨表查询需要用到

五、基于对象的跨表查询(子查询)

基于对象的跨表查询就是sql语句中的子查询,需要用到正反向查询的概念

SQL语句中的子查询:一个sql语句的执行结果是另一个sql语句的执行条件

子查询的特点是分布解决问题(套娃)

 

举例练习暂时搁置

 

六、基于双下划线的跨表查询

基于对象的跨表查询与基于双下划线的跨表查询,都是跨表查询的一种手段,是做事的两种方式,都能实现跨表查询

基于双下划线的跨表查询也需要用到正反向的概念

复制代码
    '''基于双下划线的跨表查询:适用正反向查询的概念'''
    # 1、查询书籍主键为1的图书标题、出版社名称
    # 书籍  》  出版社  》  正向一对多查询  》  用字段
    res = models.Book.objects.filter(pk=1).values('title', 'publish__title')
    print(res)
    # <QuerySet [{'title': '三国', 'publish__title': '东京出版社'}]>

    # 2、查询书籍主键为1的作者
    # 书籍  》  作者  》  正向多对多查询  》  用字段
    res1 = models.Book.objects.filter(pk=1).values('title', 'authors__name')
    print(res1)
    # <QuerySet [{'title': '三国', 'authors__name': '罗贯中'}, {'title': '三国', 'authors__name': '吴承恩'}]>

    # 3、查询曹雪芹的手机号和微信
    # 作者  》  作者详情表  》  正向一对一查询  》  用字段
    res2 = models.Author.objects.filter(name='曹雪芹').values('name', 'author_detail__phone', 'author_detail__wx')
    print(res2)
    # <QuerySet [{'name': '曹雪芹', 'author_detail__phone': '6666', 'author_detail__wx': 'caoxueqin'}]>

    # 4、查询东京出版社的所有书籍
    # 出版社  》  书籍  》  反向一对多查询  》  用名小写或者表名小写_set.all()
    res3 = models.Publish.objects.filter(title='东京出版社').values('title', 'book__title')
    print(res3)
    # <QuerySet [{'title': '东京出版社', 'book__title': '三国'}, {'title': '东京出版社', 'book__title': '水浒'}]>

    # 5、查询作者罗贯中写过的书
    # 作者  》  书籍  》 反向多对多查询  》  用名小写或者表名小写_set.all()
    res4 = models.Author.objects.filter(name='罗贯中').values('name', 'book__title')
    print(res4)
    # <QuerySet [{'name': '罗贯中', 'book__title': '三国'}, {'name': '罗贯中', 'book__title': '西游'}]>

    # 6、查询手机号是6666的作者的姓名
    # 作者详情表  》  作者  》  反向一对一查询  》 用名小写或者表名小写_set.all()
    res5 = models.AuthorDetail.objects.filter(phone='6666').values('author__name', 'phone')
    print(res5)
    # <QuerySet [{'author__name': '曹雪芹', 'phone': '6666'}]>

    # 7、查询书籍主键为2的作者的手机号
    # 书籍  》  作者  》  作者详情表  》 跨三张表的查询(正向多对多查询——正向一对一查询) 》  都用字段
    res6 = models.Book.objects.filter(pk=2).values('title', 'authors__name', 'authors__author_detail__phone')
    print(res6)
    # <QuerySet [{'title': '西游', 'authors__name': '罗贯中', 'authors__author_detail__phone': '123'}]>
复制代码

七、F与Q查询

1、 F查询

F是Django框架中db.models中sql语句一个F类,可以拿到数据库表字段的原数据,方便我们对数据库中数据的修改和查找

F查询可以操作数字和字符串

复制代码
    '''F查询'''
    from django.db.models import F

    # 1、把所有书籍提价100
    # F查询默认操作的是数字
    models.Book.objects.update(price=F('price') + 100)

    # 2、把所有的书籍标题后面都加上'经典版'
    # F查询要操作字符串需要导入两个模块
    from django.db.models.functions import Concat
    from django.db.models import Value

    models.Book.objects.update(title=Concat(F('title'), Value('经典版')))
复制代码

2、Q查询

Q查询主要用来为我们解决ORM框架操作sql语句中的(or)关系

复制代码
    '''Q查询'''

    # 1、查询书籍价格不低于300的或者标题为狂人日记的书籍
    from django.db.models import Q

    res = models.Book.objects.filter(Q(price__gt=300) | Q(title='狂人日记')).values_list()
    print(res)
    # <QuerySet [(2, '西游经典版', Decimal('400.00'), 3), (3, '水浒经典版', Decimal('500.00'), 2), (5, '狂人日记', Decimal('250.00'), None)]>

    ''' 补充:用 ,分割是and关系,用 | 分割是or关系,用 ~ 分割是not关系  '''

    res1 = models.Book.objects.filter(Q(price__gt=300), Q(title='狂人日记')).values_list()  # 查询书籍价格不低于300的和标题为狂人日记的书籍
    print(res1)
    # < QuerySet[] >  没有

    res2 = models.Book.objects.filter(~Q(price__gt=300) | Q(title='狂人日记')).values_list()  # 查询书籍价格低于300的和标题或狂人日记的书籍
    print(res2)
    # <QuerySet [(1, '三国经典版', Decimal('300.00'), 2), (4, '红楼经典版', Decimal('202.00'), 4), (5, '狂人日记', Decimal('250.00'), None)]>
复制代码

八、聚合查询和分组查询

1、聚合查询

聚合查询是根据sql语句中的五个聚合函数查询(max最大 min最小 sun求和 cont统计 avg求平均)

注意在Django语句中使用聚合函数,需要用到关键字‘aggregate’

复制代码
    '''聚合函数'''
    from django.db.models import Max, Min, Sum, Count, Avg

    # 查询书籍的最高价格,最低价格,总价格,统计个数,平均数
    res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('price'), Avg('price'))
    print(res)
    # {'price__max': Decimal('500.00'), 'price__min': Decimal('202.00'), 'price__sum': Decimal('1652.00'), 'price__count': 5, 'price__avg': 330.4}

    # 还可以给聚合函数起别名
    res1 = models.Book.objects.aggregate(max_price=Max('price'))
复制代码

2、分组查询

对应sql语句中弄得group by,ORM使用分组需要用到关键字annotate

sql语句中的分组回顾

# 严格模式
set global sql_mode='STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH,only_full_group_by'

mysql从5.7以后,默认开启group by的严格模式。

但是严格模式下不方便我们查看数据库中的具体数值对应的内容,为此,我们可以适当配置
方式一:更改my.cnf(windows下是my.ini)中的sql_mode参数,去掉:only_full_group_by。
方式二:分组拼接


ORM操作分组查询

复制代码
    '''分组查询'''
    # 分组查询需要用到annotate

    # 1、统计每一本书的作者个数
    # 书籍  》  作者  》  正向多对多查询  》  用字段
    res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')  # 给Count()起别名作values的键
    print(res)
    # <QuerySet [{'title': '三国经典版', 'author_num': 2}, {'title': '西游经典版', 'author_num': 1}, {'title': '水浒经典版', 'author_num': 1}, {'title': '红楼经典版', 'author_num': 0}, {'title': '狂人日记', 'author_num': 0}]>

    # 2、统计每个出版社最便宜的书
    # 出版社  》  书籍  》  方向一对多查询  》  表名小写
    res1 = models.Publish.objects.annotate(min_price=Min('book__price')).values_list('title', 'book__title', 'min_price')
    print(res1)
    # <QuerySet [('东京出版社', '三国经典版', Decimal('300.00')), ('南京出版社', '西游经典版', Decimal('400.00')), ('西京出版社', '红楼经典版', Decimal('202.00')), ('北京出版社', None, None)]>

    # 3、统计不只一个作者的图书
    # 翻译:统计图书的作者个数,且作者个数至少有两个
    # 3.1 查询每个图书的作者个数
    res2 = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    print(res2)
    # <QuerySet [{'title': '三国经典版', 'author_num': 2}, {'title': '西游经典版', 'author_num': 1}, {'title': '水浒经典版', 'author_num': 1}, {'title': '红楼经典版', 'author_num': 0}, {'title': '狂人日记', 'author_num': 0}]>

    # 3.2 查询作者个数大于1的图书
    # 利用filter过滤
    res3 = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title', 'author_num')
    print(res3)
    # < QuerySet[{'title': '三国经典版', 'author_num': 2}] >
复制代码

 

九、事务

 

 

 

posted @   三三得九86  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示