文章目录
数据准备
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.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,否则返回False
1.2 all( )
查询所有记录
models.User.objects.all()
:返回一个QuerySet对象
res = models.Book.objects.all()
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
1.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_name,password=pwd)
----filter 括号内可以携带多个参数,参数之间相当于是and
关系,必须全部为真才能匹配成功
res = models.User.objects.filter()
print(res)
# <QuerySet [<Book: 书籍_三国志 对象>, <Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
所以,filter 括号内不传筛选条件,相当于获得所有表中的记录对象
1.4 get(**kwargs)–不推荐使用
res = models.Book.objects.get(pk=1)
print(res)
# 书籍_三国志 对象
其返回的直接就是 筛选之后的对象
但是,当筛选的对象不存在时,直接报错!!
1.5 exclude(**kwargs)
它包含了与所给筛选条件不匹配的对象
res = models.Book.objects.exclude(pk=1)
print(res)
# <QuerySet [<Book: 书籍_三国演义 对象>, <Book: 书籍_水浒传 对象>]>
1.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: 书籍_三国志 对象>]>
1.7 reverse( )
对查询结果反向排序,通常只能在具有已定义顺序的QuerySet上调用(在model类的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: 书籍_三国志 对象>]>
1.8 distinct()
注意:ORM 去重 会考虑主键,也就是说不能直接对 QuerySet 中的单个对象去重
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=5)
# 查询年份是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(kucun__gt=F('maichu'))
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", "聚合字段别名)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9fGNacNe-1656064040144)(图片.assets/分组查询 1.png)]
from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values("dept", "avg")
"""
这里需要注意的是annotate分组依据就是他前面的值,
如果前面没有特点的字段,则默认按照ID分组,
这里有dept字段,所以按照dept字段分组
"""
多表分组查询
关键字:annotate(聚合字段别名=聚合函数('正反向查询规则')).values("字段 1", "聚合字段别名")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJnrSuzW-1656064040146)(图片.assets/分组查询 2.png)]
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来表示分组后想要查找的字段值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C209usJ3-1656064040147)(图片.assets/分组查询 3.png)]