django学习笔记
今日内容概要
- 正反向查询进阶操作
- 聚合查询
- 分组查询
- F与Q查询
- ORM查询优化
- ORM常见字段类型
- ORM常见字段参数
- ORM事务操作
- 多对多三种创建方式详解
今日内容详细
正反向查询进阶操作
基于双下划线的跨表查询的结果也可以是完整的数据对象,手上有条件所在的表可以不被models点,直接点最终的目标数据对应的表。
1.查询主键为1的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name','book__title')
print(res)
2.查询主键为3的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=1).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)
聚合查询
聚合函数有Max, Min, Sum, Avg, Count。
聚合函数 | 说明 |
---|---|
Max | 取最大值 |
Min | 取最小值 |
Sum | 求和 |
Avg | 求平均 |
Count | 计数 |
聚合查询:
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)
'''没有分组之前如果单纯的使用聚合函数、需要关键字aggregate、默认整体就是一组'''
分组查询
分组有一个特性,默认只能够直接获取分组的字段,想要获取其他字段需要使用方法,我们也可以忽略掉该特性,将sql_mode中only_full_group_by配置移除即可。
分组两种方式:
1.按照整条数据分组
models.Book.objects.annotate() # 按照一条条书籍记录分组
2.按照表中某个字段分组
models.Book.objects.values('title').annotate() # 按照annotate之前values括号中指定的字段分组
示例1:统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
print(res)
示例2:统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)
示例3:统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title','author_num')
print(res)
'''filter在annotate前面则是where(筛选)、在annotate后面则是having(过滤)'''
示例4:查询各个作者出的书的总价格
res = models.Author.objects.annotate(book_sum_price = Sum('book__price')).values('name','book_sum_price')
print(res)
F与Q查询
F查询:查询条件不是自定义的而是来自于表中其他字段
当表中已经有数据的情况下,添加额外的字段,需要指定默认值或者可以为null。
方式1:设置字段值允许为空
IntegerField(verbose_name='销量',null=True)
方式2:设置字段默认值
IntegerField(verbose_name='销量',default=1000)
方式3:在终端中直接给出默认值
使用F查询需要先导入模块
from django.db.models import F
①查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)
②将所有书籍的价格上涨1000块
models.Book.objects.update(price=F('price') + 1000)
③将所有书籍名称加上爆款后缀
#models.Book.objects.filter(pk=5).update(title=F('title') + '爆款') # 针对字符串数据无法直接拼接
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.filter(pk=5).update(title=Concat(F('title'), Value('爆款')))
Q查询:可以改变filter括号内多个条件之间的逻辑运算符
from django.db.models import Q
res = models.Book.objects.filter(pk=1, publish_id=3) # 默认是and关系
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关系
res = models.Book.objects.filter(~Q(pk=1) | Q(publish_id=3)) # 波浪号是not关系
Q查询还可以将查询条件的字段改为字符串形式
from django.db.models import Q
q_obj = Q()
q_obj.connector = 'or' # q对象默认的多个条件也是and关系 可以修改为or
q_obj.children.append(('pk', 1))
q_obj.children.append(('publish_id', 3))
res = models.Book.objects.filter(q_obj)
ORM查询优化
django orm查询默认都是惰性查询,只有orm的语句在后续的代码中真正需要使用的时候才会执行。还自带limit分页功能,是为了减轻数据库端以及服务端的压力。
ORM查询优化之only与defer
res = models.Book.objects.only('title','price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
only会将括号内填写的字段封装成一个个数据对象,对象在点击括号内的字段的时候不会再走数据库查询,但是对象也可以点击括号内没有的字段,只不过每次都会走数据库。
res = models.Book.objects.defer('title', 'price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
defer与only刚好相反,数据对象点括号内出现的字段,每次都会走数据库查询;
数据对象点括号内没有的字段,不会走数据库查询。
ORM查询优化之select_related与prefetch_related
res = models.Book.objects.select_related('authors')
for obj in res:
print(obj.publish.name)
select_related括号内只能接收外键字段自动连表,得出的数据对象在点表中数据的时候都不会再走数据库查询。
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
prefetch_related底层其实是子查询,将查询之后的结果一次性封装到数据对象中,用户在使用的时候是感觉不出来的。
ORM常见字段类型
ORM字段和mysql里字段的对应关系。
ORM | MYSQL |
---|---|
AutoField() | int auto_increment |
CharField() | 必须提供max_length参数,对应的数据库中是varchar类型 |
IntergerField() | int |
DecimalField() | decimal |
DateField() | date |
DateTimeField() | datetime |
BigIntergerField() | bigint |
BooleanField() | 传布尔值 存0和1 |
TextField() | 存储大段文本 |
FileField() | 存储文件数据 自动找指定位置存储 字段存具体路径 |
EmailField() | 本质还是varchar类型 |
关系字段
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)
ORM常见字段参数
参数 | 说明 |
---|---|
primary_key | 主键 |
max_length | 最大字符长度 |
verbose_name | 对应的名字 |
null | 是否可以为空 |
default | 默认值 |
max_digits | 数字允许的最大位数 |
decimal_places | 小数的最大位数 |
unique | 字段的值唯一 |
db_index | 是否为字段设置索引 |
auto_now | 更新修改数据时间 |
auto_now_add | 数据添加的时间 |
to | 设置关联的表 |
to_field | 设置关联的字段 |
db_constraint | 是否创建外键约束,默认True |
related_name | 修改正向查询的字段名,之后就可以使用修改后的字段名,类似于起别名 |
choices:当字段数据的可能性是可以完全列举出来的时候应该考虑使用该参数
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice)
userinfo_obj.get_gender_display() # 有对应关系就拿 没有还是本身
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相似
ORM事务操作
MySQL事务:四大特性(ACID),原子性,一致性,独立性,持久性。一个事务是一个整体,要么同时成功要么同时失败。
事务隔离级别:
1.read uncommitted(未提交读)
事务中的修改即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这一现象也称之为"脏读"
2.read committed(提交读)
大多数数据库系统默认的隔离级别
一个事务从开始直到提交之前所作的任何修改对其他事务都是不可见的,这种级别也叫做"不可重复读"
3.repeatable read(可重复读) # MySQL默认隔离级别
能够解决"脏读"问题,但是无法解决"幻读"
所谓幻读指的是当某个事务在读取某个范围内的记录时另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录会产生幻行,InnoDB和XtraDB通过多版本并发控制(MVCC)及间隙锁策略解决该问题
4.serializable(可串行读)
强制事务串行执行,很少使用该级别
mysql开启事务:start transcation;
mysql回滚事务:rollback;
mysql停止事务:commit;
mysql保留点:savepoint
django的事务操作:
from django.db import transaction
try:
with transaction.atomic():
pass # 多条ORM语句
except Exception as e:
print(e)
# with一结束事务就结束了,自动提交和回滚
多对多三种创建方式
1.自动创建
authors = models.ManyToManyField(to='Author')
'''
优点:orm自动创建第三张表
缺点:第三张表扩展性差
'''
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")
'''
优点:第三张表扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法
'''
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了