基于上一张表继续做进阶操作 这次我们不能以第一个条件作为条件 不能通过首要条件去获得结果怎样操作呢?
'''正反向查询进阶操作'''
1.查询主键为1的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title')
print(res)
2.查询主键为3的书籍对应的作者姓名及书名
res1 = models.Author.objects.filter(book__pk=3).values('name', 'book__title')
print(res1)
3.查询Lebron的作者的电话号码和地址
res2 = models.AuthorDetail.objects.filter(author__name='Lebron').values('phone', 'address')
print(res2)
4.查询南方出版社出版的书籍名称和价格
res3 = models.Book.objects.filter(publish__name='南方出版社').values('title', 'price')
print(res3)
5.查询Like写过的书的名称和日期
res4 = models.Book.objects.filter(authors__name='Like').values('title', 'publish_time')
print(res4)
6.查询电话是110的作者姓名和年龄
res5 = models.Author.objects.filter(author_detail__phone=110).values('name', 'age')
print(res5)
7.查询主键为1的书籍对应的作者电话号码
res6 = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
print(res6) # 两种解法
res7 = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
print(res7)
这里的聚合函数其实跟我们之前的SQL聚合函数一样
(Max最大、Min最小、Sum总和、Avg平均、count统计)
但是不一样的是我们使用这些聚合函数的时候需要导入 from django.db.models. import Max, Min, Sum, Avg, Count
from django.db.models import Max, Min, Sum, Avg, Count
1.查找所有书籍中价格最高的书籍
res = models.Book.objects.aggregate(我是世界上最贵的书=Max('price'))
print(res) # 没有分组之前如果单纯的时候聚合函数 需要关键字aggregate
2.获取书籍中最贵的 最便宜的 全部需要多少钱 平均一本书多少钱 总共有几本书
res1 = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res1)
'''通过 别名 = 聚合函数的方法可以取一个别名'''
分组在SQL中是group by 而在ORM中需要用到annotate
但是分组的特性还是一样的 默认只能够直接获取分组的字段
1.统计每一本书的作者个数
from django.db.models import Max, Min, Sum, Avg, Count
方式1:
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
print(res)
'''由于我的是MAC本 Win本在这里正常是报错的 因为Sql-mode有个限制默认是only_full_group_by 要把这个配置给去掉
set global sql_mode = 'STRICT_TRANS_TABLES'; 即可不报错 '''
方式2:
res1 = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id', 'book_num')
print(res1)
'''
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)
3.统计不止一个作者的图书
res1 = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title', 'author_num')
print(res1)
4.查询各个作者出的书的总价格
res2 = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name', 'book_sum_price')
print(res2)
首先我们先给我们的书籍表格再添加两条数据(库存以及已销售)
storage_num = models.IntegerField(verbose_name='库存数')
sale_num = models.IntegerField(verbose_name='已销售')
但是出现报错了
we can't do that (the database needs something to populate existing rows)
现在这张表已经有数据了 再添加两个新的字段 没有数据值 它的数据值呢 ?
两种方式 一个默认值 一个退出不执行这个命令再去修改(所以我们得给他加上数据)
storage_num = models.IntegerField(verbose_name='库存数', null=True)
sale_num = models.IntegerField(verbose_name='已销售', default=1000)
'''F查询'''
1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=sale_num) # 这是我们正常套路
'''所以我们在查询表格查询条件查询字段都来自一张表 就需要用到F查询
F查询:查询条件不是自定义的而是来自于表中其他字段'''
from django.db.models import F
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.update(title=F('title') + '爆款')
'''这个时候也报错了 因为我们不能直接添加字符串 如果我们要添加字符串类型的数据要导入模块'''
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.filter(pk=2).update(title=Concat(F('title'), Value('爆款')))
'''Q查询'''
res = models.Book.objects.filter(pk=1, publish_id=3) # 默认链接符 and
'''Q查询:可以改变filter括号内多个条件之间的逻辑运算符 还可以将查询条件的字段改为字符串形式'''
from django.db.models import Q
res1 = models.Book.objects.filter(Q(pk=1), Q(publish_id=3)) # 逗号隔开还是and关系
res2 = models.Book.objects.filter(Q(pk=1) | Q(publish_id=3)) # 管道符隔开是or关系
res3 = models.Book.objects.filter(~Q(pk=1) | Q(publish_id=3)) # 波浪号隔开是not关系
我们都知道filter可以查询多个数据 但是它的默认链接符号是and但是我们想改变它怎么做呢?
# 注意查看一下区别 就知道Q功能的强大了
res = models.Book.objects.filter('pk'=1)
res = models.Book.objects.filter(pk=1)
'''两个意义完全不一样 但是我们也想这个字符串的查找到IDpk=1的值怎么做呢 ?'''
q_obj = Q() # 设置一个Q对象
q_obj.connector = 'or' # 可以修改类型 AND OR 默认and
q_obj.children.append(('pk', 1))
q_obj.children.append(('publish_id', 3))
q_obj.children.append(('pk', 2)) # 可以无限添加条件
res1 = models.Book.objects.filter(q_obj)
print(res1)
ORM查询优化
res = models.Book.objects.all()
# 按理来说我们这一句话有走数据库查询的呀 但是怎么可以打印呢? (确保日志打开 主要功能打印SQL语句)
'''Django ORM默认是惰性查询 当orm的语句在后续的代码中真正需要使用的时候才会执行'''
print(res)
'''通过打印出来的SQL语句我们可以发现orm自带Limit分页功能 主要是用来减轻数据库以及服务端的压力'''
res = models.Book.objects.values('title', 'price') # 结果是结果集套字典
for obj in res:
print(obj.title) # 但是我们现在想实现通过句点符的方式来获取数据 字典只能使用Get
'''这个时候我们就可以使用到ORM查询关键字 Only Defer select_related prefetch_related 四个关键字'''
Only
res = models.Book.objects.only('title', 'price')
for obj in res:
print(obj.title) # 这个时候我们可以通过only拿到数据
print(obj.price)
print(obj.storage_num) # 我们点击我们没有的属性呢 ?
'''但是我们点 我们没有的数据 发现也可以获取到 我们看SQL语句发现走了5次数据库这是为什么呢?
将括号内字段封装成一个个数据对象 当我们点有的数据不会走数据库查询 没有的就会走数据库查询
'''
Defer
res = models.Book.objects.defer('title', 'price')
for obj in res:
# print(obj.title) # 这个时候我们可以通过only拿到数据
# print(obj.price)
print(obj.storage_num) # 我们点击我们没有的属性呢 ?
'''Defer与Only刚好相反 数据对象点击括号内出现的字段 每次都会走数据库查询
数据对象点击括号内没出现的字段 每次都不会走数据库查询
'''
select_related
我们先拿到所有的书籍对象 再获取所有的出版社名称
res = models.Book.objects.all()
for obj in res:
print(obj.publish.name)
'''这个时候我们发现走了很多次数据库查询 要是1000条呢 ? 这样的话数据库压力就比较大了 容易崩 就可以用到了ORM关键字select-related '''
res = models.Book.objects.select_related('publish')
for obj in res:
print(obj.publish.name)
'''select_related括号内只能接收外键字段(一对多 一对一 多对多报错) 自动连表inner join 得出的数据对象在点击表中数据的时候都不会再走数据库查询'''
prefetch_related
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
'''prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的'''
什么是事务?
事务是用户定义的一系列数据库操作 这些操作可以视为一个完成的逻辑处理工作单元
要么全部执行 要么全部不执行 是不可分割的工作单元
事务的产生
数据库中的数据是共享资源 因此数据库系统通常要支持多个用户的或不同应用程序的访问
并且各个访问进程都是独立执行的 这样就有可能出现并发存取数据的现象 为了避免数据库的不一致性
这种处理机制称之为“并发控制”,其中事务就是为了保证数据的一致性而产生的一种概念和手段(事务不是唯一手段)
事务的四大特征
为了保证数据库的正确性与一致性事务具有四个特征
原子性Atomicity
事务的原子性保证事务中包含的一组更新操作是原子的 不可分割的 不可分割是事务最小的工作单位
所包含的操作被视为一个整体 执行过程中遵循 要么全部执行 要不都不执行 不存在一半执行 一半未执行的情况
一致性Consistency
事务的一致性要求事务必须满足数据库的完整性约束
且事务执行完毕后会将数据库由一个一致性的状态变为另一个一致性的状态 事务的一致性与原子性是密不可分的
隔离性Isolation
事务的隔离性要求事务之间是彼此独立的 隔离的 及一个事务的执行不可以被其他事务干扰
持久性Durability
事物的持续性也称持久性 是指一个事务一旦提交 它对数据库的改变将是永久性的
因为数据刷进了物理磁盘了 其他操作将不会对它产生任何影响
在ORM中怎样使用事务呢 ?
from django.db import transaction
try:
with transaction.atomic():
pass # 多条ORM语句
except Exception as e:
print(e)
'''因为数据跟MySQL的事务一直 请查询博主以前文章 http://t.csdn.cn/8siwC
名称 | 含义 |
---|---|
AutoField() | Int自增列 必须填入参数 primary_key=True 当model中如果没有自增列 则自动会创建一个列名为id的列 |
CharField() | 字符类型 必须提供max_length参数 max_length表示字符长度 |
IntegerField() | 一个整数类型 范围在 -2147483648 to 2147483647 (一般不用它来存手机号(位数也不够) 直接用字符串存) |
BigIntegerField() | 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 |
DateField() | 日期字段 日期格式 YYYY-MM-DD 相当于Python中的datetime.date()实例 |
DateTimeField() | 日期时间字段 格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 相当于Python中的datetime.datetime()实例 |
DecimalField() | 10进制小数 参数 max_digits 小数总长度 decimal_places,小数位长度 |
EmailField() | 字符串类型 Django Admin以及ModelForm中提供验证机制 |
BooleanField() | 布尔值类型 传布尔值存数字0或1 |
TextField() | 文本类型 存储大段文本 |
FileField() | 字符串 路径保存在数据库 文件上传到指定目录 参数 upload_to = " " 上传文件的保存路径 storage = None 存储组件 默认django.core.files.storage.FileSystemStorage |
ForeignKey() | 外键类型在ORM中用来表示外键关联关系 一般把ForeignKey字段设置在 '一对多’中’多’的一方 ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系 |
OneToOneField() | 一对一字段 通常一对一字段用来扩展已有字段 通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联 |
ManyToManyField() | 简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下 下面的方法才可使用create add set remove clear |
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) # 回调之前的init方法
def db_type(self, connection):
return 'char(%s)' % self.max_length
info = MyCharField(max_length=32) # 这样就定义了自己的char字段
名称 | 含义 |
---|---|
max_length | 最大长度 |
verboses_name | 别名 |
auto_now | 配置上auto_now=True,每次更新数据记录的时候会更新该字段 |
auto_now_add | 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库 |
null | 用于表示某个字段可以为空 |
default | 为该字段设置默认值 |
max_digits | 数字中允许的最大的数字位数 |
decimal_places | 数字的小数位数 |
unique=True | 如果设置为unique=True 则该字段在此表中必须是唯一的 |
db_index=True | 如果db_index=True 则代表着为此字段设置索引 |
to | 设置要关联的表 |
to_field | 设置要关联的表的字段 |
related_name | 外键取别名 |
on_delete 当删除关联表中的数据当前表与其关联的行的行为
models.CASCADE 级联操作 当主表中被连接的一条数据删除时 从表中所有与之关联的数据同时被删除
models.SET_NULL 当主表中的一行数据删除时 从表中所有与之关联的数据的相关字段设置为null 此时注意定义外键时 这个字段必须可以允许为空
models.PROTECT 当主表中的一行数据删除时 由于从表中相关字段是受保护的外键 所以都不允许删除
models.SET_DEFAULT 当主表中的一行数据删除时 从表中所有相关的数据的关联字段设置为默认值 此时注意定义外键时 这个外键字段应该有一个默认值
models.SET() 当主表中的一条数据删除时 从表中所有的关联数据字段设置为SET()中设置的值 与models.SET_DEFAULT相似 只不过此时从表中的相关字段不需要设置default参数
models.DO_NOTHING 什么都不做 一切都看数据库级别的约束 注数据库级别的默认约束为RESTRICT 这个约束与django中的models.PROTECT相似
chocies 当字段数据的可能性是可以完全列举出来的时候 应该考虑使用该参数
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, 'other'),
)
gender = models.IntegerField(choices=gender_choice)
user_obj = models.UserInfo.objects.filter(pk=1).first()
print(user_obj.gender) # 获取的是真实数据
print(user_obj.get_gender_display())
user_obj1 = models.UserInfo.objects.filter(pk=2).first()
user_obj2 = models.UserInfo.objects.filter(pk=3).first()
user_obj3 = models.UserInfo.objects.filter(pk=4).first()
print(user_obj1.get_gender_display())
print(user_obj2.get_gender_display())
print(user_obj3.get_gender_display()) # 如果没有则按照真实数据返回
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")
优点:扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点点赞收藏+关注
谢谢支持 !!!