Django-ORM 单表查询
数据准备
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来表示分组后想要查找的字段值