Day 09 基于ORM记录查询
写在前面的话
- 模板语言:每个语言的web框架都会有模板语言,django—》dtl
- 模板语言的渲染,是再后端完成的
- 用php写前端(html,css,js)(不对的)
- 前后端分离:前后端交互,统一全用json格式
- 前端只专注于写前端(vue,react:前端工程化),后端只专注于写后端(提供接口,交互json格式数据)
一、聚合查询
1.1 聚合函数的导入
from django.db.models import Avg,Sum, Min, Max, Sum, Count
1.2 聚合函数的使用
# 1 计算所有图书的平均价格
# aggregate结束,已经不是queryset对象了
book = Book.objects.all().aggregate(Avg('price'))
print(book, type(book)) # 得到是 字段名__聚合函数小写:内容的 字典
# 起别名
book = Book.objects.all().aggregate(avg=Avg('price'))
print(book)
---------------------------------------------------------
{'price__avg': 46.8796} <class 'dict'>
{'avg': 46.8796}
# 2 计算总图书数
book_count_dic = Book.objects.all().aggregate(count=Count('id'))
print(book_count_dic)
----------------------------------------------------------
{'count': 50}
# 3 计算最低价格的图书
book_price_min = Book.objects.all().aggregate(min=Min('price'))
print(book_price_min)
----------------------------------------------------------
{'min': Decimal('1.30')}
# 4 计算所有书的价格
book_price_sum = Book.objects.all().aggregate(sum=Sum('price'))
print(book_price_sum)
-----------------------------------------------------------
{'sum': Decimal('2343.98')}
二、分组查询
重点:annotate() 内写聚合函数
- values在前表示group by的字段
- values在后表示取某几个字段
- filter在前表示where
- filter在后表示having
# 1、查询出版社id大于15的出版社id,以及出书平均价格
# select publish_id,avg(price) from app01_book where publish_id>1 group by publish_id;
price_avg = Book.objects.values('publish__id').filter(publish__id__gt=15).annotate(avg=Avg('price')).values(
'publish__name', 'price', 'name', 'avg')
li = []
for i in price_avg:
if i['name'] not in li:
li.append(i['name'])
# print(i['publish__name'], i['price'], i['avg'])
print(len(li))
--------------------------
16
与数据库查看的数据相同
# 2
# 统计每一个出版社的最便宜的书
author_min_price = Book.objects.values('publish__id').annotate(min=Min('price')).values('publish__name', 'min')
for i in author_min_price:
print(i['publish__name'], i['min'])
# 3
# 统计每一本以py开头的书籍的作者个数:
startswith_hu = Book.objects.values('publish__id').filter(name__startswith='湖').annotate(count=Count('publish__id')).values('publish__name', 'count')
for i in startswith_hu:
print(i['publish__name'], i['count'])
# 4
# 作者数量大于2的图书名字和价格
author_gtt = Publish.objects.values('book__id').annotate(count=Count('book__id')).filter(count__gte=2).values('book__name','book__price', 'count')
for i in author_gtt:
print(i['book__name'], i['book__price'], i['count'])
三、F和Q查询
F查询:
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
from django.db.models import F, Q
# 修改id为25 书籍的价格 + 100
book35 = Book.objects.filter(id=25).update(price=F('price')+100)
# 查询id数大于价格的书籍
books = Book.objects.filter(id__gt=F('price')).values( 'name','id','price')
for i in books:
print(i)
注意:F和Q只适用于 Quire_SET对象,也就是说get,first,last 是不适用的
Q查询:制造 与或非的条件
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象
与(|)
# 查询 作者id为8 并且 出版社id为3 的书籍名称和出版社名字
res = Book.objects.filter(Q(author__id=8) & Q(publish__id=3)).values('name', 'publish__name')
print(res)
-------------------------------
<QuerySet [{'name': '西藏自治区的老奶奶', 'publish__name': '尼日利亚出版社'}]>
或(&)
# 查询作者id 为4 或者8的 作者,书籍的名称,和出版社名字
res = Book.objects.filter(Q(author__id=4) | Q(author__id=8)).values('author__name', 'name', 'publish__name')
for i in res:
print(i)
---------------------------------
{'author__name': '王婷', 'name': '甘肃省不相信眼泪', 'publish__name': '尼泊尔出版社'}
{'author__name': '王婷', 'name': '甘肃省不相信眼泪', 'publish__name': '沙特阿拉伯出版社'}
{'author__name': '马晶', 'name': '西藏自治区的老奶奶', 'publish__name': '尼日利亚出版社'}
{'author__name': '马晶', 'name': '西藏自治区的老奶奶', 'publish__name': '多米尼加共和国出版社'}
非(~)
res = Publish.objects.filter(~Q(book__name='湖南省不相信眼泪') & Q(book__publish__id=2)).values('book__name', 'id','book__id')
for i in res:
print(i)
四、原生SQL语句
方案一:用的比较少
from django.db import connection
cursor = connection.cursor()
cursor.execute("""SELECT * from app01_book where id = %s""", [1])
# row = cursor.fetchone()
row = cursor.fetchall()
print(row)
本质与 pymysql 执行方法没啥不同
- 先创建连接
- 创造游标
- 游标接口执行SQL语句
- 返回结构 fetchall 或者 fethone
方案二:用的多
# 查询id大于 45 的所有书籍信息
books = Book.objects.raw('select * from app01_book where id >45')
for i in books:
print(i.id, i.name, i.price)
# 跨表查询
res = Book.objects.raw(
'select app01_book.id, app01_book.name as name2,app01_author.name from app01_book inner join app01_author on app01_book.author_id=app01_author.id')
for i in res:
print(i.name2, i.id, i.name)
五、defer和only
# defer和only(查询优化相关)
# only保持是book对象,但是只能使用only指定的字段
# books = models.Book.objects.all().only('name')
# print(books[0].name)
# print(books[0].price) # 能出来,但是会再执行一句SQL语句
# books = models.Book.objects.all().only('name')
#
# print(books[0].__dict__)
books = models.Book.objects.all().defer('name','price')
print(books[0].__dict__)
六、事务(请求,装饰器,局部)
事务的特性ACID
- 原⼦性: 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么完全不起作⽤;
- ⼀致性: 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;
- 隔离性: 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的;
- 持久性: ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何 影响。
事务的三个粒度
1、局部使用
from django.db import transaction
with transaction.atomic(): # 都在事物中,要么都成功,要么都失败
author_detail=models.AuthorDetail.objects.create(addr='xxx',phone='123',sex=1)
# raise Exception('抛了异常')
author=models.Author.objects.create(name='llqz',age=19,author_detail=author_detail)
2、视图函数装饰器,这一个视图函数都在一个事物中
@transaction.atomic
def index(request):
return HttpResponse('ok')
3、整个http请求,在事物中,在setting.py中配置
DATABASES = {
'default': {
...
'PORT': 3306,
'ATOMIC_REQUEST': True,
}
}
'ATOMIC_REQUEST': True,
设置为True统一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败)。