python学习Day61
Day 61
今日内容概要
- 正反向查询进阶操作
- 聚合查询
- 分组查询
- 给表中添加新字段的方式
- F与Q查询(重要)
- ORM查询优化(面试重点)
- only与defer(主要适用于单表操作)
- select_related与prefetch_related(主要适用于多表操作)
- ORM事物操作
- ORM常见字段类型
- ORM常见字段参数
- 多对多三种创建方式
今日内容详细
1.正反向查询进阶操作
1.查询主键为1的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name','book__title')
print(res)
2.查询主键为2的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=2).values('name', 'book__title')
print(res)
3.查询jason的作者的电话号码和地址
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','addr')
print(res)
4.查询南方出版社出版的书籍名称和价格
res = models.Book.objects.filter(publish__name='南方出版社').values('title','price')
print(res)
5.查询jason写过的书的名称和日期
res = models.Book.objects.filter(authors__name='jason').values('title','publish_time')
print(res)
6.查询电话是110的作者姓名和年龄
res = models.Author.objects.filter(author_detail__phone=110).values('name','age')
print(res)
7.查询主键为1的书籍对应的作者电话号码
res = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
print(res)
res = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
print(res)
2.聚合查询
聚合函数:max(最大值)、min(最小值)、sum(求和)、avg(平均值)、count(计数)
#需要先导入聚合函数才能使用
#没有分组之前如果想单纯的使用聚合函数 需要关键字aggregate
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res)
3.分组查询
"""
分组有一个特性:默认只能够直接获取分组的字段 其他字段需要使用方法。可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可
cmd>mysql>
show variables like '%mode%'; #可以看到sql_mode中有一个only_full_group_by
set global sql_mode='STRICT_TRANS_TABLES'; #即可移除只用这一个
"""
1.统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
print(res)
#结果显示<QuerySet [{'title': 'python入门', 'author_num': 3}]
1.1.统计每一个出版社有多少本书
res1 = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res1)
#结果显示<QuerySet [{'publish_id': 1, 'book_num': 2}]
"""
1.按照整条数据分组
models.Book.objects.annotate() 按照一条条书籍记录分组
2.按照表中某个字段分组
models.Book.objects.values('title').annotate() 按照annotate之前values括号中指定的字段分组
"""
2.统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)
#结果显示<QuerySet [{'name': '北方出版社', 'min_price': Decimal('20000.00')}]
3.统计不止一个作者的图书
'''filter在annotate前面则是where 在annotate后面则是having'''
res=models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title','author_num')
print(res)
4.查询各个作者出的书的总价格
res = models.Author.objects.annotate(book_sum_price = Sum('book__price')).values('name','book_sum_price')
print(res)
4.给表中添加新字段的方式
当表中已经有数据的情况下添加新的字段需要指定一些参数可以有三种方式:
方式1:设置字段值允许为空 null=True
#stprage_num=models.IntegerField(verbose_name='库存数',null=True)
方式2:设置字段默认值 default=1000
#stprage_num=models.IntegerField(verbose_name='库存数',default=1000)
方式3:在终端中直接给出默认值
#输入迁移命令时选择1 provide a default value给出默认值
5.F与Q查询(重要)
'''F查询:查询条件不是自定义的而是来自于表中其他字段'''
from django.db.models import F
1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)
2.将所有书籍的价格上涨1000块
models.Book.objects.update(price=F('price') + 1000)
3.将所有书籍名称加上爆款后缀
#models.Book.objects.filter(pk=5).update(title=F('title') + '爆款') 会出错
from django.db.models.functions import Concat # 针对字符串数据无法直接拼接需再导两个模块(合并)
from django.db.models import Value # 针对字符串数据无法直接拼接需再导两个模块(值)
models.Book.objects.filter(pk=5).update(title=Concat(F('title'), Value('爆款')))
'''Q查询:可以改变filter括号内多个条件之间的逻辑运算符(默认是and);还可将查询条件的字段改为字符串形式'''
from django.db.models import Q #导模块
1.查询主键是1或出版社id是3的数据
#res=models.Book.objects.filter(Q(pk=1),Q(publish_id=3))#逗号分割是and关系
res=models.Book.objects.filter(Q(pk=1)|Q(publish_id=3))#管道符分割是or关系
print(res)
2.查询主键不是1或出版社id是3的数据
res = models.Book.objects.filter(~Q(pk=1) | Q(publish_id=3)) # 波浪号是not关系
print(res)
3.将查询跳进的字段改为字符串形式(重要!!可以和用户交互!可以获取用户要搜索的条件来搜索)
q_obj=Q()
q_obj.connector='or' #Q对象默认的多个条件也是and关系 可以修改为or
q_obj.children.append(('pk',1))
q_obj.children.append(('publish_id',1))
res=models.Book.objects.filter(q_obj)
print(res)
6.ORM查询优化(面试重点)
django orm默认都是惰性查询
当orm的语句在后续的代码中真正需要使用的时候才会执行(print(res))
django orm自带limit分页
减轻数据库端以及服务端的压力
#以下操作需打开查看ORM底层SQL语句,方便看到底层SQL语句执行了什么操作
6.1.ORM查询优化—only与defer(主要适用于单表操作)
1.查询所有书籍信息
【only】
res = models.Book.objects.only('title', 'price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time) #对象点括号内没有的其他字段时也会显示
#显示结果是 python入门 29888.80 2022-09-05 14:45:49.977836+00:00...
'''
only会将括号内填写的字段封装成一个个数据对象:
数据对象点击括号内出现的字段 不会走数据库查询
数据对象点击括号内没有的字段 每次都会走数据库查询
缺点是:因为走的for循环,如果数据库中有一万条数据,就会走一万条数据库查询 会崩掉!
'''
【defer】
res = models.Book.objects.defer('title', 'price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time) #对象点括号内没有的其他字段时也会显示
'''
defer与only相反:
数据对象点击括号内出现的字段 每次都会走数据库查询
数据对象点击括号内没有的字段 不会走数据库查询
'''
6.2.ORM查询优化之select_related与prefetch_related(主要适用于多表操作)
【select_related】
res = models.Book.objects.select_related('publish')
for obj in res:
print(obj.publish.name)
'''
select_related括号内只能接收外键字段(一对多 或 一对一)内部自动连表
得出的数据对象在点击表中数据的时候都不会再走数据库查询
多对多会报错!!
会连表然后把大表的数据全部给你
'''
【prefetch_related】
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
'''
prefetch_related底层其实是子查询
将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的
先子查询查到两个表里的数据然后再合并封装给你
'''
——————————————————————————————————————————————————————————————————
select_related与prefetch_related结果一样,但底层不一样。
连表与子查询各有优势:
当数据不大时,连表需要2s 子查询需要2s 那就选择连表
当数据特别大时,连表的效率会非常低。
7.ORM事物操作
事务:ACID
事务隔离级别:脏读 幻读 不可重复读 ...
原生SQL: start transaction\rollback\commit\savepoint
ORM使用事物需导入模块:
from django.db import transaction
try:
with transaction.atomic():#atomic原子性
pass # 可以写多条ORM语句
except Exception as e: #可加入异常捕获
print(e)
8.ORM常见字段类型
#普通字段类型:
AutoField() 定义属性
CharField() 字符类型(必须提供max_length字符长度参数 最大255)
IntegerField() 整型(一般不用来存手机号,手机号用字符串存)
BigIntegerField() 整型(更大、范围更多的)
DateField() 日期字段
DateTimeField() 日期时间字段
DecimalField() 10进制小数
EmailField() 邮箱类型
BooleanField() 传布尔值自动存数字0或1
TextField() 存储大段文本(比CharField还要大的大段文本)
FileField() 存储文件数据(自动找指定位置存储 字段存具体路径)
#外键字段类型:
ForeignKey() 一对多字段
OneToOneField() 一对一字段
ManyToManyField() 多对多字段
ORM还支持自定义字段:
class MyCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super().__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
return 'char(%s)' % self.max_length
info = MyCharField(max_length=32)
9.ORM常见字段参数
#普通字段参数:
max_length 字符长度
verboses_name Admin中显示的字段名称
auto_now 每次更新数据记录时会更新该字段
auto_now_add 创建数据记录时会把当前时间添加到数据库
null 表示某个字段可以为空
default 为该字段设置默认值
max_digits 小数总长度(在10进制小数类型中的参数)
decimal_places 小数位长度(在10进制小数类型中的参数)
unique=True 表示该字段在此表中必须是唯一
db_index=True 为此字段设置索引
choices 当字段数据可能完全列举出来的时候 应该考虑使用该参数
(性别、学历、成绩等)
_________________________________________________________
【choices】
eg:
models.py中
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice)
tests.py中
user_obj=models.UserInfo.objects.filter(pk=1).first()
print(user_obj.gender)#查找数据库中的真实数据
print(user_obj.get_gender_display())#查找提前写好的对应关系,如没有则按真实数据显示
#结果显示为1则是真实数据关系 结果显示为男性则是对应关系显示
_________________________________________________________
#外键字段参数:
1.to 设置要关联的表
2.to_field 设置要关联的表的字段
3.related_name 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'
4.on_delete 当删除关联表中的数据时,当前表与其关联的行的行为
(1)models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
(2)models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
(3)models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
(4)models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
(5)models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
(6)models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
10.多对多三种创建方式
1.自动创建
authors = models.ManyToManyField(to='Author')
优点:第三张表自动创建
缺点:第三张表扩展性差(当想添加字段时没办法添加)
2.手动创建
class Book(models.Model):
pass
class Author(models.Model):
pass
class Book2Author(models.Model):#自己创建第三张关系表
book_id = models.ForeignKey(to="Book")#自己创外键
author_id = models.ForeignKey(to="Author")#自己创外键
优点:第三张表扩展性强
缺点:无法使用正反向查询以及多对多的四个方法
3.半自动创建(折中方式):最灵活的方式
class Book(models.Model):
authors = models.ManyToManyField(to='Author',
through='Book2Author' # 通过哪个表
through_fields=('book_id','author_id')# 通过哪两个字段
)
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeignKey(to="Author")
优点:扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法
作业
1.整理今日内容及博客
2.尝试编写图书管理系统书籍的增删改查功能