目录
数据准备
查询关键字
1. QuerySet 对象
first方法
last方法
values方法
values_list 方法
count()
exists()
2. all()
3. filter(**kwargs)
4. get(**kwargs) 不推荐使用
5. exclude(*kwargs)
6. order_by(*field)
7. reverse()
8. distinct()
基于双下划线的模糊查询
__in >>> 成员运算
__range >>> 范围查询
字段中包含某些字符
F查询与Q查询
F查询
根据F查询统一修改字段的值
Q查询
聚合查询
分组查询
单表分组查询
多表分组查询
数据准备
models.py 文件
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
publish_time = models.DateField(auto_now_add=True)
def __str__(self):
return '书籍_%s 对象'% self.title
tests.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_ORM.settings")
import django
django.setup()
from app01 import models
models.Book.objects.create(title='三国志',price='123.23')
models.Book.objects.create(title='三国演义',price='133.23')
models.Book.objects.create(title='水浒传',price='223.23')
查询关键字
1. QuerySet 对象
当使用一些查询语句之后, 获得的返回值会是,<QuerySet [...]>形式
QuerySet 对象中放着符合查询条件的整个记录的对象,注意是一个个的对象 ,并且是整个记录的, 不是某个字段的值, 该方法会获取所有的记录对象, 并且ORM会自动做一个limit限制展示(默认在21个左右)这个列表可以索引取值,但是不能用负数. 推荐使用的内置方法是res.first(), res.last()
只要是QuerySet对象, 就可以.query查看SQL语句
first方法
获取QuerySet对象列表中的首个对象
last方法
获取QuerySet对象列表中的最后一个对象
values方法
只拿到指定的字段的值__ 字典类型. 注意, 返回的是一个QuerySet对象
res = models.Book.objects.values('title','price')
print(res)
# <QuerySet [{'title': '三国志', 'price': Decimal('123.23')},
# {'title': '三国演义', 'price': Decimal('133.23')},
# {'title': '水浒传', 'price': Decimal('223.23')}]>
values_list 方法
只拿到指定字段的值, ____列表套元组类型, 注意: 返回的是一个QuerySet对象
res = models.Book.objects.values_list('title','price')
print(res)
"""
<QuerySet [
('三国志', Decimal('123.23')),
('三国演义', Decimal('133.23')),
('水浒传', Decimal('223.23'))
]>
"""
count()
返回匹配查询(QuerySet)的对象数量
exists()
如果QuerySet包含数据,就返回True,否则fanhui False
2. all()
查询所有记录
models.User.objects.all() 返回一个QuerySet对象
res = models.Book.objects.all()
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
3. filter(**kwargs)
查询单条记录核心代码
当我们筛选的字段为主键时, pk可以代替所有主键的具体名称, 而不用具体输入主键的字段
# 去数据库中查询数据
from app01 import models
res = models.User.objects.filter(pk=1)
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>]>
# models.User.objects.filter(**{......})
models.User.objects.filter(username=user_name)匹配成功的返回值是一个列表,匹配失败返回值就是None
filter(username=user_namepassword=pwd)-----filter 括号内可以携带多个参数, 参数之间相当于'and'的关系,必须全部为真才能匹配成功
res = models.User.objects.filter()
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
所以,filter括号内不传筛选条件, 相当于获得表中所有的记录对象
4. get(**kwargs) 不推荐使用
res = models.Book.objects.get(pk=1)
print(res)
# 书籍_三国志 对象
5. exclude(*kwargs)
它包含了与所给筛选条件不匹配的对象
res = models.Book.objects.exclude(pk=1)
print(res)
# <QuerySet [<Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
6. order_by(*field)
对查询结果排序, 默认升序, 降序则需要在字段前加上一个符号
注意,可以同时放多个字段,按照从左到右的顺序排序, 越靠近左侧,优先级越高, 当遇到结果相同的字段会按照右边的字段依次排序
res = models.Book.objects.order_by('price')
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
res = models.Book.objects.order_by('-price')
print(res)
# <QuerySet [<Book: 书籍_水浒传 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_三国志 对象>]>
7. reverse()
对查询结果反向排序,通常只能在具有已定义顺序的QuerySet上调用,(在modle类的Meta中指定ordering或者调用order_by()方法
res = models.Book.objects.order_by('price')
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
res = models.Book.objects.order_by('price').reverse()
print(res)
# <QuerySet [<Book: 书籍_水浒传 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_三国志 对象>]>
8. distinct()
注意, ORM去重,会考虑主键,也就是说不能直接对Query Set中的单个对象去重
models.Book.objects.create(title='三国志',price='123.23')
res = models.Book.objects.distinct()
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>, <Book: 书籍_三国志 对象>]>
res = models.Book.objects.values('title').distinct()
print(res)
# <QuerySet [{'title': '三国志'}, {'title': '三国演义'}, {'title': '水浒传'}]>
基于双下划线的模糊查询
__gt >>>大于
__lt >>>小于
__gte >>>大于等于
__lte >>> 小于等于
# 1.查询年龄大于20的用户
res = models.User.objects.filter(age__gt = 20)
__in >>> 成员运算
# 2.查询年龄是18、22、25的用户
res = models.User.objects.filter(age__in=[18, 22, 25])
__range >>> 范围查询
# 3.查询年龄在18到26之间的用户
res = models.User.objects.filter(age__range=[18, 26]) # 包含18和26
字段中包含某些字符
__contains >>> 区分大小写
__icontains >>> 不区分大小写
# 4.查询姓名中包含字母j的用户
res = models.User.objects.filter(name__contains='j')
res = models.User.objects.filter(name__icontains='j')
__year >>> 按照年份筛选数据
__month >>> 按照月份筛选数据
注意数据的日期格式, 可以查询一些其他的日期格式
# 查询月份是5月的数据
res = models.User.objects.filter(op_time__month=8)
# 查询年份是22年的数据
res = models.User.objects.filter(op_time__year=2022)
补充:
__startswith >>> 区分大小写
__endswith >>> 区分大小写
__regex >>>
F查询与Q查询
F查询
在上边所有的例子中, 我们构造的过滤器都只是将字段值与某个我们自己设定的常量作比较, 如果我们要对两个字段的值作比较, 那该怎么做呢?
Django提供了F()来做这样的比较 , F()的实例可以在在查询中引用字段, 来比较用一个model实例中两个不同字段的值
使用方法: .filter(筛选字段的方式=F("字段"))
例如: 查询卖出数量大于库存数量的商品
from django.db.models import F
res = models.Book.objects.filter(storage_-gt=F('sold'))
print(res)
根据F查询统一修改字段的值
例如: 将每个商品的价格都提高100元
from django.db.models import F
res = models.Book.objects.update*price = F('price')+100)
print(res)
针对字符串不能直接拼接, 需要导入别的模块
from django.db.models import F
from django.db.models.functions import Concat
res = models.Book.objects.update(name=Concat(F('name')),Value('爆款'))
print(res)
Q查询
filter()方法中逗号隔开的条件是'与'的关系. 如果需要执行更加复杂的查询,(OR语句), 就可以使用Q对象
例如: AND-----逗号,
res = Book.objects.filter(title='三国演义',number=1000)
print(res)
print(res.query)
# <QuerySet [<Book: Book object>]>
# select * from app01_book where(`app01_book`.`title` = 三国演义 AND `app01_book`.`number` = 1000)
from django.db.models import Q
res = Book.objects.filter(Q(title='三国演义'),Q(number=1000))
print(res.query)
# select * from app01_book where(`app01_book`.`title` = 三国演义 AND `app01_book`.`number` = 1000)
OR----管道符 |
from django.db.models import Q
res = Book.objects.filter(Q(title='三国演义')|Q(number=1000))
print(res.query)
# select * from app01_book where(`app01_book`.`title` = 三国演义 OR `app01_book`.`number` = 1000)
NOT---波良号 ~
from django.db.models import Q
res = Book.objects.filter(~Q(title='三国演义')|Q(number=1000))
print(res.query)
# select * from app01_book where(NOT (`app01_book`.`title` = 三国演义) OR `app01_book`.`number` = 1000)
Q的高阶用法
需求: 我们使用filter()传入的参数都必须是手动输入已经存在的, 那么如何将输入筛选条件写的灵活呢?
condition = input('请输入你要筛选的条件:')
data = input('请输入你要筛选的值:')
res = Book.objects.filter(condition = data)
很明显,这样写是不行的, 因为book表中并没有condition字段,所以会报错,可以借助Q对象来实现
condition = input('请输入你要筛选的条件:')
data = input('请输入你要筛选的值:')
q = Q()
q.children.append((condition,data))
res = Book.objects.filter(q)
print(res.query)
# condition = title
# data = 三国演义
# SELECT * FROM `app01_book` WHERE `app01_book`.`title` = 三国演义
也可以多次添加条件,并且修改链接多个条件的方法, 默认是AND
q = Q()
q.children.append(("title","三国演义"))
q.children.append(("nuber__gt","500"))
res = Book.objects.filter(q)
print(res.query)
# SELECT * FROM `app01_book` WHERE (`app01_book`.`title` = 三国演义 AND `app01_book`.`number` > 500)
q = Q()
q.connector='or'
q.children.append(("title","三国演义"))
q.children.append(("nuber__gt","500"))
res = Book.objects.filter(q)
print(res.query)
# SELECT * FROM `app01_book` WHERE (`app01_book`.`title` = 三国演义 OR `app01_book`.`number` > 500)
聚合查询
内置函数导入
from django.db.models import Avg,Sum.Max,Min,Count()
关键字: aggregate('筛选的字段名')
最终返回的是一个字典类型的数据
求书籍的总价和最高价
from django.db.models import *
res = Book.objects.aggregate(Max('price'),Sum('price'))
print(res)
# {'price__max': Decimal('300.50'), 'price__sum': Decimal('1366.83')}
分组查询
单表分组查询与跨表分组查询中聚合函数括号内传入的值是不一样的!!
单表分组查询
关键字: annotate(annotate(聚合字段别名=聚合函数('表内')).values('字段1','聚合字段别名)
from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values("dept", "avg")
"""
这里需要注意的是annotate分组依据就是他前面的值,
如果前面没有特点的字段,则默认按照ID分组,
这里有dept字段,所以按照dept字段分组
"""
多表分组查询
关键字:annotate(聚合字段别名=聚合函数('正反向查询规则')).values('字段1','聚合字段别名')
from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
总结
value里边的参数对应的是sql语句中的select要查找显示的字段
filter里边的参数相当于where或者having里边的筛选条件
annotate本身表示group by 的作用. 前面找寻分组依据, 内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值
ORM查询优化
only 与 defer
Django ORM默认是惰性查询,当ORM语句在后续的代码中真正需要使用的时候才会执行。
如果你明确不需要这个数据库列(或在大部分情况里不需要),使用 defer() 和 only() 来避免加载它们.
obj = models.Book.objects.only('title', 'price')
for i in obj:
print(i.title)
print(i.price)
print(i.sale
only会查询括号内的字段并封装成一个对象返回,使用该对象不会在通过数据库查询。但是查询括号里没有的字段,还是会走数据库。
obj = models.Book.objects.defer('title', 'price')
for i in obj:
print(i.title)
print(i.price)
print(i.sale)
而defer和only相反。
select_related 和 prefetch_related
select_related()
select_related(*fileds)
返回一个QuerySet, 他将跟随外键关系, 再执行查询时选择额外的相关对象数据, 这是一个性能升器, 他导致一个更复杂的单一查询, 意味着以后使用外键关系将不需要数据库查询.
b = models.Book.objects.select_related('publish') # 拼表
for i in b:
print(i.title)
print(i.price)
print(i.sale)
c = models.Author.objects.select_related('author_info')
print(c) # 只支持一对一和一对多关系
prefetch_related()
prefetch_related(*lookups)
返回一个QuerySet, 他将在一个批次中自动检索每个指定查询的相关对象
这与select_related有类似的目的, 二者都是为了阻止因访问相关对象而引起的数据库查询潮,但策略却完全不同
select_related 的工作方式是创建一个SQL链接, 并在SELECT语句中包含相关对象的字段, 处于这个原因,select_related在同一个数据据库查询中得到相关对象. 然而为了避免因跨越'many'关系进行链接而产生更大的结果集, select_related仅限于单值关系>>>>外键和一对一.
prefetch_related则对每个关系进行单独的查找, 并在python中进行joining. 这使得它除了支持select_related的外键和一对一关系, 还可以预约多对多和多对一的对象, 这是用select_Related无法做到.
https://docs.djangoproject.com/zh-hans/4.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related
事务操作
django默认的事务行为是自动提交, 触发事务正在执行, 每个查询将会马上自动提交到数据库.
django自动使用事务或还原点,以确保需要多次查询的ORM操作的一致性, 特别是delete() 和 update()操作.
官方文档:数据库事务 | Django 文档 | Djangohttps://docs.djangoproject.com/zh-hans/4.1/topics/db/transactions/#deactivating-transaction-management