'''
2种设置外键字段的方式
1. 实际字段指定id publish_id=id
2. 虚拟字段指定对象 publish=publish_obj
'''
models.Book.objects.create(title='论语', price='333.33', publish_id=1)
models.Book.objects.create(title='孟子', price='444.44', publish_id=2)
models.Book.objects.create(title='老子', price='555.55', publish_id=2)
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='三字经', price='666.66', publish=publish_obj)
models.Book.objects.filter(pk=1).delete() # 级联删除
models.Book.objects.filter(pk=2).update(publish_id=1)
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)
# 增 create
# 实际
models.Book.objects.create(publish_id=id, **kwargs)
# 虚拟
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(publish=publish_obj, **kwargs)
# 删 delete
models.Book.objects.filter(pk=1).delete() # 级联删除
# 改 update
# 实际
models.Book.objects.filter(pk=1).update(publish_id=id)
# 虚拟
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
# 查
# 实际
# 虚拟
'''
add给第三张关系表添加数据
括号内既可以传数字也可以传对象 并且都支持多个
'''
book_obj = models.Book.objects.filter(pk=3).first()
# print(book_obj.authors) # app01.Author.None(就类似于你已经到了第三张关系表了)
# book_obj.authors.add(1) # 添加一个book_id是3对应的author_id是1的记录
# book_obj.authors.add(2, 3)
book_obj = models.Book.objects.filter(pk=3).first()
authors1 = models.Author.objects.filter(pk=1).first()
authors2 = models.Author.objects.filter(pk=2).first()
authors3 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(authors1)
book_obj.authors.add(authors2, authors3)
'''
括号内既可以传数字也可以传对象 并且都支持多个
'''
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.remove(1) # 删除虚拟表中book_id是2的对应的author_id是1的记录
book_obj.authors.remove(2, 3)
'''
括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
注意!!!: set操作是一种新增操作. 执行set一上来先将括号内没有指定的全部清除, 如果有则保留, 没有则新增. 如果想修改某一个数据, 必须对源数据进行指定, 不然源数据将会被清除.
'''
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set([1, 2]) # 先删除author_id不是等于1,2的, 如果没有1, 2则添加
book_obj = models.Book.objects.filter(pk=3).first()
authors1 = models.Author.objects.filter(pk=1).first()
authors2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set([authors1, authors2])
'''
括号内不要加任何参数
'''
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear() # 清空book_id为3所对应的所有author_id
# 增 add 可以指定多个
# 提示: .authors时已经到第三张表了
# 实际
models.Book.objects.filter(pk=1).first().authors.add(1, 2)
# 虚拟
author = models.Author.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).first().authors.add(author)
# 删 remove 可以指定多个
# 提示: .authors时已经到第三张表了
# 实际
models.Book.objects.filter(pk=1).first().authors.remove(1, 2)
# 虚拟
author = models.Author.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).first().authors.remove(author)
# 改 set 放可迭代对象, 可迭代对象中可指定多个
# 注意: 是一种新增操作. 执行set一上来先将括号内没有指定的全部清除, 如果有则保留, 没有则新增. 如果想修改某一个数据, 必须对源数据进行指定, 不然源数据将会被清除.
# 实际
models.Book.objects.filter(pk=1).first().authors.set([1, 2])
# 虚拟
author = models.Author.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).first().authors.set([author])
# 清空 clear
models.Book.objects.filter(pk=1).first().authors.clear()
# 正向查询
正向查询按字段.
正向: 外键字段在的一方查不在的一方
如果返回结果多个还需要.all()
# 反向查询
反向查询按表名小写.
反向: 外键字段不在的一方查在的一方
如果返回结果多个还需要连接_set.all()
# 提示:
书籍与作者, 外键字段在书籍.
作者与作者详情, 外键字段在作者.
书籍与出版社外键字段在书籍.
'''
正向查询按字段
当你的结果可能有多个的情况下就需要加.all()
book_obj.authors.all()
如果是一个则直接拿到数据对象
book_obj.publish
author_obj.author_detail
'''
# 1. 查询书籍主键为1的出版社
book_obj = models.Book.objects.filter(pk=1).first()
print(book_obj.publish.name) # 北方出版社
# 2. 查询书籍主键为2的作者: 一本书可能被多个作者出版使用.all()
book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.authors) # app01.Author.None
print(book_obj.authors.all()) # , ]>
# 3. 查询作者jason的电话号码:
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail) # AuthorDetail object
print(author_obj.author_detail.phone) # 110
'''
反向查询按表名小写
如果查询结果可以有多个就必须需要加_set.all()
publish_obj.book_set.all()
author_obj.book_set.all()
如果查询结果只有一个的时候就不需要加_set.all()
author_detail_obj.author
'''
# 4. 查询出版社是东方出版社出版的书: 一个出版社可以出版多本书使用.all()
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set) # app01.Book.None
print(publish_obj.book_set.all()) # , ]>
# 5. 查询作者是jason写过的书: 一个作者jason可以写过多本书
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set) # app01.Book.None
print(author_obj.book_set.all()) # ]>
# 6. 查询手机号是110的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author.name) # jason
'''子查询: 基于对象的跨表查询'''
# 正向: 查询书籍主键为1的出版社
models.Book.objects.filter(pk=1).first().publish.name
# 正向: 查询书籍主键为2的作者
models.Book.objects.filter(pk=2).first().authors.all()
# 正向: 查询作者jason的电话号码
models.Author.objects.filter(name='jason').first().author_detail.phone
# 反向: 查询出版社是东方出版社出版的书
models.Publish.objects.filter(name='东方出版社').first().publish_set.all()
# 反向: 查询作者是jason写过的书
models.Author.objects.filter(name='jason').first().book_set.all()
# 反向: 查询手机号是110的作者姓名
models.AuthorDetail.objects.filter(phone=110).first().author.name
'''连表查询: 基于双下划线的连表查询'''
# 查询jason的手机号和作者姓名
# 正向
models.Author.objects.filter(name='jason').values('author_detail__phone', 'name')
# 反向
models.AuthorDetail.objects.filter(author__name='jason').values('phone', 'author__name')
# 查询书籍主键为1的出版社名称和书的名称
# 正向
models.Book.objects.filter(pk=1).values('publish__name', 'title')
# 反向
models.Publish.objects.filter(book__pk=1).values('name' 'book__title')
# 查询书籍主键为1的作者姓名
# 正向
models.Book.objects.filter(pk=1).values('authors__name')
# 反向
models.Author.objects.filter(book_pk=1).values('name')
# 查询书籍主键是1的作者的手机号
# 正向
models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
# 反向
models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
# 配置测试脚本
manage.py中3行语句. 再写入如下内容:
import django
django.setup()
# 单表操作: 数据的增删改查
# 提示: 使用pk标识主键
# 增
# 第一种方式: 自动保存
models.User.objects.create(**kwargs).
# 第二种方式: 手动.save()保存
models.User(**kwargs).save()
# 删
# 第一种方式: 统一的删除
models.User.objects.filter(**Kwargs).delete()
# 第二种方式: 单一的删除
models.User.objects.filter(**Kwargs).first().delete()
# 改
# 第一种方式: 统一的更新
models.User.objects.filter(**Kwargs).update(**kwargs)
# 第二种方式: 单一的更新
user_obj = models.User.objects.filter(**Kwargs).first()
user_obj.username = 'jason'
user_obj.save()
# 查
# 第一种方式: 获取所有.
models.User.objects.filter()
models.User.objects.all()
# 第二种方式: 获取单个对象
user_queryset = models.User.objects.filter(**kwargs)
user_obj = user_queryset.first()
user_obj = user_queryset.last()
models.User.objects.get()
# 注意:
# 返回QuerySet对象. 没有值返回空的QuerySet对象
.filter()
.all()
# 获取单个数据对象.
QuerySet.first() 在QuerySet为空的情况下返回None
QuerySet.last() 在QuerySet为空的情况下返回None
.get() 指定的条件的返回结果有多个或者不存在抛出异常(不推荐使用)
# 必知必会13条
# 返回QuerySet对象的方法
.all() 获取所有.
.filter() 过滤. 不指定参数获取所有. 查询结果不存在返回空的QuerySet对象, 布尔值False
.distinct() 去重. 必须排除主键字段 或 唯一字段才会有意义. filter无法筛选, 一般用在.values() 或 .value_list()后面
.order_by() 排序. 默认升序. 指定降序在字段前加`-`
.reverse() 反转. 只能对排序过后进行反转. 必须在.order_by()之后.
.exclude() 排除. 排除指定的, 展示所有.
# 返回特殊QuerySet对象的方法
.values() 返回QuerySet之内部列表套字典格式. 字典key就是指定的字段.
.values_list() 返回QuerySet之内部列表套元组格式. 元组中值就是指定字段对应的值, 按照指定字段顺序显示.
注意!!!: 指定的字段不存在, 将会抛出异常
# 返回数据对象的方法
.get() 直接获取数据对象. 只能指定一个筛选条件. 如果指定的筛选条件返回的结果不唯一 或者 不存在 抛出异常
.first() 获取QuerySet对象列表内第一个值. 用在QuerySet对象之后, 如果QuerySet对象为空, 再使用它返回None
.last() 获取QuerySet对象列表内最后一个值. 用在QuerySet对象之后, 如果QuerySet对象为空, 再使用它返回None
# 返回数字的方法
.count() 统计QuerySet对象列表内数据对象的个数
# 返回布尔值的方法
.exist() 没软用
# 查看内部封装的sql语句的2种形式
第一种: QuerySet.query
第二种: 执行脚本时打印日志显示到终端. 复制日志内容到settings.py中
# 神奇的双下划线查询
# 注意: 争对字段使用. 如: field__gt
__gt __lt __gte __glt
__in=[] __range=[start, stop]
__contains __icontains i全称忽略ignore
__startswith __istartswith
__endswith __iendswith
__year='2020' __year=2020
__month='1' __month=1
__day='20' __day=20
# 2种外键字段指定的方式
第一种: 实际字段指定id publish_id=id
第二种: 虚拟字段指定对象 publish=publish_obj
# 一对多外键增删改查
# 增 create
# 实际
models.Book.objects.create(publish_id=id, **kwargs)
# 虚拟
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(publish=publish_obj, **kwargs)
# 删 delete
models.Book.objects.filter(pk=1).delete() # 级联删除
# 改 update
# 实际
models.Book.objects.filter(pk=1).update(publish_id=id)
# 虚拟
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
# 查
# 实际
# 虚拟
# 多对多外键增删改查: 本质就是操作虚拟中间表(第三种表)
# 增 add 可以指定多个
# 提示: .authors时已经到第三张表了
# 实际
models.Book.objects.filter(pk=1).first().authors.add(1, 2)
# 虚拟
author = models.Author.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).first().authors.add(author)
# 删 remove 可以指定多个
# 提示: .authors时已经到第三张表了
# 实际
models.Book.objects.filter(pk=1).first().authors.remove(1, 2)
# 虚拟
author = models.Author.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).first().authors.remove(author)
# 改 set 放可迭代对象, 可迭代对象中可指定多个
# 注意: 是一种新增操作. 执行set一上来先将括号内没有指定的全部清除, 如果有则保留, 没有则新增. 如果想修改某一个数据, 必须对源数据进行指定, 不然源数据将会被清除.
# 实际
models.Book.objects.filter(pk=1).first().authors.set([1, 2])
# 虚拟
author = models.Author.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).first().authors.set([author])
# 清空 clear
models.Book.objects.filter(pk=1).first().authors.clear()
# 查询语法
# 正向查询
正向查询按字段.
正向: 外键字段在的一方查不在的一方
如果返回结果多个还需要.all()
# 反向查询
反向查询按表名小写.
反向: 外键字段不在的一方查在的一方
如果返回结果多个还需要连接_set.all()
# 提示:
书籍与作者, 外键字段在书籍.
作者与作者详情, 外键字段在作者.
书籍与出版社外键字段在书籍.
# 子查询: 基于对象的跨表查询
# 正向: 查询书籍主键为1的出版社
models.Book.objects.filter(pk=1).first().publish.name
# 正向: 查询书籍主键为2的作者
models.Book.objects.filter(pk=2).first().authors.all()
# 正向: 查询作者jason的电话号码
models.Author.objects.filter(name='jason').first().author_detail.phone
# 反向: 查询出版社是东方出版社出版的书
models.Publish.objects.filter(name='东方出版社').first().publish_set.all()
# 反向: 查询作者是jason写过的书
models.Author.objects.filter(name='jason').first().book_set.all()
# 反向: 查询手机号是110的作者姓名
models.AuthorDetail.objects.filter(phone=110).first().author.name
# 连表查询: 基于双下划线的连表查询
# 查询jason的手机号和作者姓名
# 正向
models.Author.objects.filter(name='jason').values('author_detail__phone', 'name')
# 反向
models.AuthorDetail.objects.filter(author__name='jason').values('phone', 'author__name')
# 查询书籍主键为1的出版社名称和书的名称
# 正向
models.Book.objects.filter(pk=1).values('publish__name', 'title')
# 反向
models.Publish.objects.filter(book__pk=1).values('name' 'book__title')
# 查询书籍主键为1的作者姓名
# 正向
models.Book.objects.filter(pk=1).values('authors__name')
# 反向
models.Author.objects.filter(book_pk=1).values('name')
# 查询书籍主键是1的作者的手机号
# 正向
models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
# 反向
models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
"""
介绍: 聚合查询通常情况下都是配合分组一起使用的. 如果你只想使用聚合函数, 但是不想分组, 那么就应该使用aggregate.
使用: 直接在objects后面链接.
返回: 返回字典格式的数据. 如果是对price字段求平均值, 那么返回格式是: {'price__avg': 值}
记忆跟数据库相关的模块的方法:
基本上都在django.db.models里面
如果上述没有那么应该在django.db里面
"""
from django.db.models import Max, Min, Sum, Avg, Count
# 1. 所有书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res) # {'price__avg': 555.55}
# 2. 上述方法一次性使用
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res) # {'price__max': Decimal('888.88'), 'price__min': Decimal('333.33'), 'price__sum': Decimal('3888.85'), 'price__avg': 555.55, 'pk__count': 7}
"""
分组注意事项: 分组只能拿到分组得依据. 按照什么分组就只能拿到什么组. 其他字段不能直接获取, 需要借助一些方法(聚合函数)
提示: 可以指定多个分组, 指定多个分组, 当然就可以获取多个分组之间的分组依据.
MySQl中设置全局生效得分组严格模式: set global sql_mode='only_full_group_by';
使用步骤:
第一步: 指定分组的依据
第一种情况: 默认分组. annotate直接在objects后面链接时, models后面点什么就按照什么分组.
例子: 按照书分组
models.Book.objects.annotate(sum_price=Sum)
第二种情况: 指定分组. annotate跟在values后面, values中指定什么字段就按照什么分组
例子: 按照书中的价格分组.
models.Book.objects.values('price').annotate()
第二步: 为分组的字段取别名
第三步: 在annotate后面使用values可以获取分组的依据 和 分组举和的结果
返回: 返回QuerySet对象
提示: 只要你的orm语句得出的结果还是一个queryset对象, 那么它就可以继续无限制的点queryset对象封装的方法.
特殊情况使用: 使用Count进行获取分组举和的结果应该是要对主键 或者 唯一字段进行的统计. 所以使用Count进行统计时比如: authors__id 就可以写成 authors
"""
"""
原生SQL语句: !!!注意!!! 可能作者没有写过书.
select book_id, count(author_id) from app01_book_authors group by book_id;
select app01_book.title,t1.author_count from
app01_book
left join
(select book_id, count(author_id) as author_count from app01_book_authors group by book_id) as t1
on t1.book_id=app01_book.id;
"""
# res = models.Book.objects.annotate(book_count=Count('authors__id')).values('title', 'book_count')
res = models.Book.objects.annotate(book_count=Count('authors')).values('title', 'book_count')
print(res) #
"""
原生SQL语句:
select publish_id, title, min(price) from app01_book group by publish_id;
select app01_publish.name, t1.* from
app01_publish
inner join
(select publish_id, title, min(price) from app01_book group by publish_id) as t1
on app01_publish.id=t1.publish_id;
"""
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res) #
"""
第一步: 对书分组, 求作者的个数
第二步: 过滤出作者个数大于1的.
原生SQL语句:
select book_id, count(author_id) from app01_book_authors group by book_id having count(author_id)>1;
select app01_book.title, t1.author_count
from app01_book
inner join
(select book_id, count(author_id) as author_count from app01_book_authors group by book_id having count(author_id)>1) as t1
on t1.book_id=app01_book.id;
"""
res = models.Book.objects.annotate(author_count=Count('authors')).filter(author_count__gt=1).values('title', 'author_count')
print(res) #
"""
原生SQL语句:
select app01_book_authors.author_id, sum(app01_book.price)
from app01_book
inner join
app01_book_authors
on app01_book_authors.book_id=app01_book.id
group by app01_book_authors.author_id;
select app01_author.name, t2.sum_price
from app01_author
inner join
(select app01_book_authors.author_id as author_id, sum(app01_book.price) as sum_price
from app01_book
inner join
app01_book_authors
on app01_book_authors.book_id=app01_book.id
group by app01_book_authors.author_id) as t2
on t2.author_id=app01_author.id;
"""
res = models.Author.objects.annotate(price_sum=Sum('book__price')).values('name', 'price_sum')
print(res) #
'''
# 聚合查询 aggregate
# 作用: 在不分组的前提下也能使用聚合函数
# 使用
from django.db.models import Min, Max, Sum, Avg, Count
单个: models.Book.objects.aggregate(Min('price'))
多个: models.Book.objects.aggregate(Min('price'), Max('price'), Sum('price'), Avg('price'), Count('pk'))
# 返回字典类型: {'price__avg': 值}
# 分组查询 annotate
# 作用: 分组.
# 分组注意事项: 分组只能拿到分组的依据 以及 分组举和的结果. (拓展: 可多个分组, 多个分组就含有多个分组的依据.)
# MySQL设置全局分组严格模式: set global sql_mode=only_full_group_by;
# 使用:
from django.db.models import Min, Max, Sum, Avg, Count
# 默认分组: 对models点后面的表进行分组
models.Book.objects.annotate(price_avg=Avg('price')).values('title', 'price_avg')
# 特殊情况使用: Count一般统计唯一字段 或 主键. 使用时可以简写
models.Book.objects.annotate(author_count=Count('authors__id')).values('title', 'author_count')
简写: models.Book.objects.annotate(author_count=Count('authors')).values('title', 'author_count')
# 指定分组: 分组依据publish__id
models.Book.objects.values('publish__id').annotate(publish_count=Count('title')).values('publish_count', 'publish__id', 'publish__name')
像之前我们所了解的一些过滤的例子和操作都是在针对字段值和某一个常量之间作比较,但是如果我们要针对两个字段值作比较的话就不行了,这就涉及到这个F查询了
"""
# 作用: 能够帮助你直接获取到表中某个字段对应的数据
# 使用:
from django.db.models import F
# 获取到某个字段对应的数据
F("字段__条件")
# 查询字段对应的数据是数字类型可以直接加减运算:
F('字段__条件') + 500
# 字符类型需要借助Concat和Value方法
from django.db.models.functions import Concat
from django.db.models import Value
Concat(F('字段__条件'), Value("str"))
注意: 查询字段对应的数据是字符类型不能直接进行拼接. 否则操作的字段对应的所有数据将变成空白.
"""
from django.db.models import F
# 1. 查询卖出数大于库存数的书籍
res = models.Book.objects.filter(sale__gt=F('stock'))
print(res) # , ]>
# 2. 将所有书籍的价格提升500块
res = models.Book.objects.update(price=F('price') + 500)
print(res) # .update方法返回受影响的行数 7
# 3. 将所有书的名称后面加上爆款两个字
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
# models.Book.objects.update(title=F('title') + '爆款') # 这样指定的字段title所有对应的数据全部变成空白.
"""
# 作用: filter的字段筛选条件指定多个, 默认是and连接. 要实现or或者not需要借助Q查询
# 使用:
from django.db.models import Q
Q(字段__条件=值)
# 连接条件and的3种情况
1. filter中指定多个参数逗号隔开: filter(参数1, 参数2)
2. 查询指定多个逗号隔开: filter(Q(), Q())
3. 使用&连接符: filter(Q() & Q())
# 连接条件or
filter(Q() | Q())
# 连接条件not
filter(~Q() | Q())
# Q查询的高阶用法: 让左边指定的变量形式的查询条件可以是字符串
q = Q()
q.connecter = 'or' # 指定连接符. 不指定默认and
q.children.append(Q('字段__条件', 值))
res = models.XXX.objects.filter(q)
"""
from django.db.models import Q
# 1. 查询卖出数大于100 和 价格小于900的书籍 --> 连接条件 and
# res = models.Book.objects.filter(sale__gt=100, price__lt=900)
# res = models.Book.objects.filter(Q(sale__gt=100), Q(price__lt=900))
res = models.Book.objects.filter(Q(sale__gt=100) & Q(price__lt=900))
print(res) # ]>
# 2. 查询卖出数大于100或者价格小于600的书籍 --> 连接条件 or
res = models.Book.objects.filter(Q(sale__gt=100) | Q(price__lt=600))
print(res) # , , , , , ]>
# 3. 查询卖出数不大于100或者价格小于600的书籍 --> 连接条件 not
res = models.Book.objects.filter(~Q(sale__gt=100) | Q(price__lt=600))
print(res) # ]>
# 4. Q的高阶用法: 能够将查询条件的左边变量的形式变成字符串的形式
q = Q() # 第一步: 实例化一个q对象
q.connector = 'or' # 第二步: 定义连接条件
q.children.append(('sale__gt', 100)) # 第三步: 指定字符串形式的查询字段条件, 以及范围100
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q) # 第四步: 将q对象传入filter
print(res)