'''正反向查询进阶操作''' # 1.查询主键为1的书籍对应的出版社名称及书名 res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title') print(res) # <QuerySet [{'name': '清华大学出版社', 'book__title': '女人你成功引起了我的注意'}]> # 2.查询主键为3的书籍对应的作者姓名及书名 res1 = models.Author.objects.filter(book__pk=3).values('name', 'book__title') print(res1) # <QuerySet [{'name': 'jfw', 'book__title': '凉生,我们可不可以不忧伤'}]> # 3.查询zxb的作者的电话号码和地址 res2 = models.AuthorDetail.objects.filter(author__name='zxb').values('phone', 'addr') print(res2) # <QuerySet [{'phone': 119, 'addr': '浙江'}]> # 4.查询东方出版社出版的书籍名称和价格 res4 = models.Book.objects.filter(publish__name='东方出版社').values('title', 'price') print(res4) # <QuerySet [{'title': '桃疆', 'price': Decimal('14.67')}, {'title': '三生三十十里桃花', 'price': Decimal('35.98')}, {'title': '玄霜', 'price': Decimal('54.21')}]> # 5.查询zxr写过的书的名称和日期 res5 = models.Book.objects.filter(author__name='zxb').values('title', 'publish_time') print(res5) # <QuerySet [{'title': '女人你成功引起了我的注意', 'publish_time': datetime.datetime(2022, 9, 5, 13, 16, 43, 303992, tzinfo=<UTC>)}]> # 6.查询电话是110的作者姓名和年龄 res6 = models.Author.objects.filter(author_detail__phone=110).values('name', 'age') print(res6) # <QuerySet [{'name': 'zxr', 'age': 18}]> # 7.查询主键为1的书籍对应的作者电话号码 res7 = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone') print(res7) # <QuerySet [{'phone': 120}, {'phone': 119}]> res8 = models.Author.objects.filter(book__pk=1).values('author_detail__phone') print(res8) # <QuerySet [{'author_detail__phone': 120}, {'author_detail__phone': 119}]>
聚合函数:max、min、sum、avg、count
from django.db.models import Max, Min, Sum, Avg, Count
聚合函数通常情况下是配合分组一起使用的
没有分组之前如果单纯的使用聚合函数 需要关键字aggregate
# 1.查询所有书的平均价格 res = models.Book.objects.aggregate(Avg('price')) print(res) # {'price__avg': Decimal('183.117778')}
res1 = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk')) print(res1)
报错原因
打开MySQL 输入>>>:show variables like '%mode%'; 分组有一个特性 默认只能够直接获取分组的字段 其他字段需要使用方法 我们也可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可
# 示例1:统计每一本书的作者个数 res = models.Book.objects.annotate(author_num=Count('author__pk')).values('title', 'author_num') print(res) """ 1.按照整条数据分组 models.Book.objects.annotate() 按照一条条书籍记录分组 2.按照表中某个字段分组() models.Book.objects.values('title').annotate() 按照annotate之前values括号中指定的字段分组 """ """ <QuerySet [{'title': '女人你成功引起了我的注意爆款~爆款', 'author_num': 2}, {'title': '凉生,我们可不可以不忧伤爆款~爆款', 'author_num': 1}, {'title': '桃疆爆款~爆款', 'author_num': 2}, {'title': '我是你的,生生世世爆款~爆款', 'author_num': 2}, {'title': '国民大侦探爆款~爆款', 'author_num': 1}, {'title': '十宗罪爆款~爆款', 'author_num': 1}, {'title': '三生三十十里桃花爆款~爆款', 'author_num': 1}, {'title': '玄霜爆款~爆款', 'author_num': 1}, {'title': '一生一世爆款~爆款', 'author_num': 1}]> """
annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所有使用前要从django.db.models引入Avg,Max,Count,Sum(首字母大写))。
分组后,用values取值,则返回值是QuerySet书籍类型里面为一个个字典 分组后,用values_list取值,则返回值是QuerySet数据类型里面为一个个元组
MySQL中的limit相当于ORM中的QuerySet数据类型的切片。
annotate() 里面放聚合函数 1.values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。 2.values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。 3.filter放在 annotate 前面:表示where条件 4.filter放在annotate后面:表示having
跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询
models后面点什么 就按什么分组 author_num 是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数
1.统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('author__pk')).values('title', 'author_num') print(res) 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括号中指定的字段分组 """ """ <QuerySet [{'title': '女人你成功引起了我的注意', 'author_num': 2}, {'title': '凉生,我们可不可以不忧伤', 'author_num': 1}, {'title': '桃疆', 'author_num': 2}, {'title': '我是你的,生生世世', 'author_num': 2}, {'title': '国民大侦探', 'author_num': 1}, {'title': '十宗罪', 'author_num': 1}, {'title': '三生三十十里桃花', 'author_num': 1}, {'title': '玄霜', 'author_num': 1}, {'title': '一生一世', 'author_num': 1}]> """
2.统计每个出版社卖的最便宜的书的价格
res1 = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price') print(res1) """ <QuerySet [{'name': '清华大学出版社', 'min_price': Decimal('28.98')}, {'name': '北方出版社', 'min_price': Decimal('34.98')}, {'name': '南方出版社', 'min_price': Decimal('56.29')}, {'name': '东方出版社', 'min_price': Decimal('14.67')}]> """
3.统计不止一个作者的图书
'''filter在annotate前面则是where 在annotate后面则是having''' res = models.Book.objects.annotate(author_num=Count('author__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) """ <QuerySet [{'name': 'zxr', 'book_sum_price': Decimal('159.25')}, {'name': 'wyn', 'book_sum_price': Decimal('1365.63')}, {'name': 'zxb', 'book_sum_price': Decimal('1383.86')}, {'name': 'jfw', 'book_sum_price': Decimal('103.95')}]> """
能够帮助你直接获取到列表中某个字段对应的数据 注意: 在操作字符串类型的数据的时候, F不能够直接做到字符串的拼接
1.查询卖出书大于库存数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num')) print(res)
将所有书籍的价格上涨1000快
res = models.Book.objects.update(price=F('price') + 1000)
给所有书籍名称后面加上爆款后缀
针对字符串数据无法直接拼接
models.Book.objects.filter().all().update(title=F('title') + '爆款') # 针对字符串数据无法直接拼接 # 导入Concat模块 from django.db.models.functions import Concat # 导入Value模块 from django.db.models import Value models.Book.objects.filter().all().update(title=Concat(F('title'), Value('~爆款')))
可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询的结果改为字符串形式
filter()等方法中的关键字参数查询都是一起进行"and",如果你需要执行更复杂的查询(列如ORM语句),你可以使用Q对象。
查询卖出数大于100并且价格小于60的书籍
res = models.Book.objects.filter(Q(sale_num__gt=100), Q(price__lt=600)) print(res)
查询卖出数大于130或者价格小于30的书籍
res = models.Book.objects.filter(Q(sale_num__gt=130) | Q(price__lt=30)) print(res)
查询卖出数不是大于80或者价格大于600的书籍
res = models.Book.objects.filter(~Q(sale_num__gt=80) | Q(price__gt=600)) print(res)
总结
, and关系 | or关系 ~ not关系 Q 默认是and,可以进行修改 q = Q() q.connector = 'or'
可以将查询的结果改为字符串形式
先产生一个对象 q = Q() 修改默认的对象 q.connector = 'or' 添加查询条件,第一个写一个字符串的字段名 第二个元素写一些具体的数值 q.children.append(('pk', 1)) q.children.append(('publish_id', 3)) 添加好以后q对象支持直接用filter进行筛选 res = models.Book.objects.filter(q) print(res)
django orm默认都是惰性查询 当orm的语句在后续的代码中真正需要使用的时候才会执行 django orm自带limit分页 减轻数据库端以及服务端的压力
res = models.Book.objects.only('title','price') for obj in res: print(obj.title) print(obj.price) print(obj.publish_time) """ only会将括号内填写的字段封装成一个个数据对象 对象在点击的时候不会再走数据库查询 但是对象也可以点击括号内没有的字段 只不过每次都会走数据库查询 如果看到有人只有only查询的话 尽量不要去点括号内没有的字段 因为它每次都会去查询一次 """
res = models.Book.objects.defer('title', 'price') for obj in res: # print(obj.title) # print(obj.price) print(obj.publish_time) """ defer与only刚好相反 数据对象点击括号内出现的字段 每次都会走数据库查询 数据对象点击括号内没有的字典 不会走数据库查询 """
res = models.Book.objects.all() for obj in res: print(obj.publish.name) # 频繁走数据库查询 res = models.Book.objects.select_related('publish') """ select_related括号内只能接收外键字段(一对多 一对一) 它会在内部自动拼表 得出的数据对象在点击表中数据的时候都不会再走数据库查询 """
for obj in res: print(obj.publish.name) """ prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的 """ res = models.Book.objects.prefetch_related('publish') for obj in res: print(obj.publish.name)
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) publish_time = models.DateTimeField(auto_now=True) publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) author = models.ManyToManyField(to='Author') ''' 当表中已经有数据的情况下 添加新的字段需要指定一些参数 1.设置字段值存于为空 null=True 2.设置字段默认值 default=1000 3.在终端直接给出默认值 ''' storage_num = models.IntegerField(verbose_name='库存数', null=True) sale_num = models.IntegerField(verbose_name='卖出数目', default=1000) def __str__(self): return f'对象:{self.title}'
django.db.utils.OperationalError: (2002, "Can't connect to server on '127.0.0.1' (10061)")
解决方法:查看数据库是否启动,导致这个错误很可能是MySQL停止了服务