今日内容回顾(Django之查询相关操作)
今日内容回顾
正反向查询进阶操作
# 1.查询主键为2的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=2).values('name','book__title')
print(res) # <QuerySet [{'name': '南方出版社', 'book__title': 'python'}]>
# 2.查询主键为3的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=3).values('name','book__title')
print(res)
"""
<QuerySet [{'name': 'tom', 'book__title': 'golang'},
{'name': 'egon', 'book__title': 'golang'},
{'name': 'jason1号', 'book__title': 'golang'}]>
"""
聚合查询
聚合查询通常情况下都是配合分组一起使用的,只要是跟数据库相关的模块
基本上都在django.db.models或django.db里面
# 通过一些聚合函数配合分组使用得到所需要的数据,没有分组之前也可以通过aggregate关键字配合使用。
# 聚合函数:max、min、sum、avg、count
# 使用前需要先导入
from django.db.models import Max,Min,Sum,Avg,Count
res = models.Book.objects.aggregate(Count('pk'),Avg('price'),Max('price'))
print(res) # {'pk__count': 10, 'price__avg': Decimal('32533.326000'), 'price__max': Decimal('38888.88')}
分组查询
分组有一个特性,默认只能够直接获取分组的字段,其它字段需要使用方法。
我们也可以忽略该特性,在sql_mode中only_full_group_by欸之移除即可
#分组查询 需要使用到分组关键字 annotate
"""
1.按照整条数据分组
models.Book.objects.annotate()
根据表名,将该表内的一条条数据进行分组
2. 按照表中某个字段分组
models.Book.objects.values('字段名').annotate()
按照annotate前面的values括号内填写的指定字段分组
"""
# 按照整条数据分组
# 示例1,统计每一本书的作者个数
res = models.Book.objects.annotate(A_C= Count('authors__PK')).values('A_C')
print(res)
# 示例2,统计出每个出版社卖的最便宜的书
res = models.Publish.objects.annotate(P_P = Min('book__price')).values('name','P_P')
print(res)
"""
<QuerySet [{'name': '南方出版社', 'P_P': Decimal('28888.88')},
{'name': '北方出版社', 'P_P': Decimal('18888.88')},
{'name': '西方出版社', 'P_P': Decimal('26666.66')}
, {'name': '东方出版社', 'P_P': Decimal('36666.66')},
{'name': '中原出版社', 'P_P': Decimal('38888.88')}]>
"""
# 按照表中某个字段分组
# 统计每个出版设出了几本书
res = models.Book.objects.values('publish_id').annotate(数量=Count('pk')).values('数量','publish__name')
print(res) # <QuerySet [{'publish__name': '南方出版社', '数量': 1},
# {'publish__name': '北方出版社', '数量': 5},
# {'publish__name': '西方出版社', '数量': 2},
# {'publish__name': '东方出版社', '数量': 1},
# {'publish__name': '中原出版社', '数量': 1}]>
F与Q查询
-
F查询
查询条件不是自定义的而是来至表中其它字段
# F查询 # 1.查询卖出数大于库存数的书籍 """ 能够直接获取到表中某个字段对应的数据 """ from django.db.models import F # res = models.Book.objects.filter(stock__gt=F('sales_volume')) # print(res) # <QuerySet [<Book: 对象:golang>, <Book: 对象:linux>, <Book: 对象:c>, <Book: 对象:java>, <Book: 对象:javascript>, <Book: 对象:django>]> # 2.将所有书籍的价格提升500块 models.Book.objects.update(price=F('price')+500) # 3.将所有书的名称后面加上爆款两个字 models.Book.objects.filter(pk=5).update(title=F('title') + '爆款') """ 在操作字符类型的数据的时候 F不能够直接做到字符串的拼接 """ from django.db.models.functions import Concat from django.db.models import Value # 需要使用Concat及Value models.Book.objects.update(title=Concat(F('title'),Value("爆款"))) -
Q查询
可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询条件的字段改为字符串形式
# Q查询 可以改变filter括号内多个条件之间的逻辑运算符 # 1.查询卖出数大于3000或者价格大于38000的书籍 res = models.Book.objects.filter(sales_volume__gt=3000,price__gt=38000) print(res) # <QuerySet []> # filter括号内的参数是and关系 # 导入Q用Q来操作 from django.db.models import Q # | 管道符or 关系 # res = models.Book.objects.filter(Q(sales_volume__gt=3000)|Q(price__gt=38000)) # print(res) # <QuerySet [<Book: 对象:c爆款>, <Book: 对象:mysql爆款>]> # ~ 波浪号为not关系 res = models.Book.objects.filter(~Q(sales_volume__gt=3000) | Q(price__gt=38000)) print(res) # Q查询 还可以将查询条件的字段改为字符串形式 q = Q() q.connector = 'or' q.children.append(('sales_volume__gt', 3000)) q.children.append(('price__gt', 38000)) res = models.Book.objects.filter(q) # 默认还是and关系 print(res) # <QuerySet [<Book: 对象:c爆款>, <Book: 对象:mysql爆款>]>
ORM查询优化
django orm自带limit分页,便于减轻数据库以及服务端的压力
django orm默认都是惰性查询,当orm的语句在后续的代码中真正需要使用的时候才会执行。
# ORM查询优化之only与defer
# 1. only
book_obj = models.Book.objects.only('title','price')
for obj in book_obj:
print(obj.title)
print(obj.price)
print(obj.publish_time)
"""
在使用only关键字时,only会将括号内填写的字段封装成一个个数据对象
对象点击的时后不会再去数据库查询
如果点击没有在括号内的字段,则每次都会重新去数据库查询找该字段对应的数据
可以简单的理解为,筛选出括号内的字段数据并保存起来,便于以后调用
不在括号内的字段数据每次调用则需要重新查询数据库
"""
# 2. defer
book_obj = models.Book.objects.defer('title','price')
for obj in book_obj:
print(obj.title)
print(obj.price)
print(obj.publish_time)
"""
defer与only刚好相反
数据对象点击括号内出现的字段,每次都会走数据库查询
数据对象点击括号内没有的字典,不会走数据库查询
可以简单的理解为,筛选出括号内的其它字段数据并保存起来,便于以后调用
在括号内的数据每次调用则需要重新查询数据库
"""
# ORM查询优化之select_related与prefetch_related
res = models.Book.objects.all()
for obj in res:
print(obj.publish.name) # 频繁走数据库查询
# 3.select_related
res = models.Book.objects.select_related('publish')
for i in res:
print(i.publish.name)
"""
select_related括号内只能接收外键字段(一对一、一对多)
自动连表,得出的数据对象在点击表中数据的时候都会在走数据库查询
"""
# 4. prefetch_rlated
res = models.Book.objects.prefetch_related('publish')
for i in res:
print(i.publish.name)
"""
prefetch_related底层其实是子查询
将查询之后的结果也一次性封装到数据对象中,用户在使用的时候是感觉不出来的。
"""
事务操作
事务:ACID
原子性:不可分割的最小单位
一致性:跟原子性是相辅向成
隔离性:事务之间互相不干扰
持久性:事务一旦确人永久性
开启事务transaction 回滚rollback 确认commit 返回上一次savepoint
from django.db import transaction
try:
with transaction.atomic():
pass # 多条ORM语句
except Exception as e:
print(e)
模型层常见字段
AutoField------主键字段 (primary_key=True)
- int 自增列 必须填入(primary_key=True)
BigAutoField(AutoField)
- bigint自增列,必须填入参数 (primary_key=True)
CharField---varchar
verbose_name---字段的注释
max_length---长度
- 字符类型,必须提供max_length参数表示字符长度
IntegerField---int
- 整数列(有负号),-2147483648 ~ 2147483647
BigIntegerField---bigint
- 长整型(有负号的) -9223372036854775808 ~ 9223372036854775807
DecimalField
- 10进制小数
- 参数
max_digits=8 小数总长度,表示八位
decimal_places=2 小数位长度,表示俩位
EmailField---varchar(254)
- 字符串类型,Django Admin以及ModelForm中提供验证机制
DateField---date
DateTimeField---datetime
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
BooleanField---布尔值类型
该字段传布尔值(Flase/True) 数据库里面存0/1
TextField---文本类型
该字段可以用来存大段内容(文章、博客...)没有字数限制
FileField---字符类型
upload_to = "/data"
给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中---/data/a.txt
# django 除了提供很多字段类型之外,还支持自定义字段
class MyCharField(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
# 重新调用父类的init方法
super().__init__(max_length=max_length,*args,**kwargs) #一定要是关键字的形式传入
def db_type(self,connection)
# 返回真正的数据类型及各种约束条件
return 'char(%s)'%self.max_length
# 自定义字段使用
myfield = MyCharField(max_length=32,null=True)
模型层常见参数
max_length
max_length=32
-- 设置字段字符的长度
verboses_name
verboses_name="注释"
-- 添加字段的注释
auto_now
auto_now=True
-- 每次修改数据的时候都会自动更新当前时间
auto_now_add
auto_now_add=True
-- 只在创建数据的时候记录创建时间后续不会自动修改了
null
null=True
-- 用于表示某个字段可以为空
defaulf
defaulf="默认值"
-- 为该字段设置默认值
max_digits
decimal_places
unique=True
-- 如果等于True则表示该字段在此表中是唯一的
db_index=True
如果db_index=True 则代表该字段设置索引
choices
当字段数据的可能性是可以完全列举出来的时候,可以考虑使用该参数
如:性别、学历、成绩...
如下:
class UserInfo(models.Model):
username= models.CharField(max_length=32)
gender_choice=((1,男),(2,女))
gender = models.IntegerField(choices=gender_choice)
userinfo_obj.get_gender_display()
to
设置要关联的表 如 to="表名"
to_field
设置要关联的表的字段,默认不写关联的就是另一张表的主键字段
related_name
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为
django1.x中默认级联更新级联删除,
django2.x、3.x、4.x,需要自己手动指定定外键字段的级联更新级联删除。
# 示例:on_delete = models.CASCADE
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
什么都不做,一切都看数据库几倍的约束,注意数据库级别的默约束为RESTTRICT
这个约束与django中的models.PROTECT相似
多对多的三种创建方式
-
自动创建
authors = models.ManyToManyField(to="Author") # 优:第三张表自动创建,无需自己手动创建 缺:第三张表扩展性差 -
手动创建
class Book(models.Model): pass class Author(models.Model): pass class Book2Author(models.Model): book_id = models.Foreignkey(to="Book") author_id = models.ForeginKey(to="Author") # 优:第三张表扩展性强 # 缺:无法使用正反向查询及多对多的四个方法 add、set、remove、clear -
半自动创建
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") # 优:扩展性强并支持正反向查询 # 缺:无法使用多对多四个方法 add、set、remove、clear

浙公网安备 33010602011771号