9、Django_ORM_数据的创建以及增删改查
一、查询集QuerySet
- 什么是查询集?
查询集:从数据库查询得到的模型对象集合QuerySet
,是一个列表 - 什么是过滤器?
过滤器:基于查询集得到的结果上进一步进行条件筛选过滤结果
二、过滤器
返回list/多个结果的过滤器
-
values = model名称.objects.all()
:返回所有数据;返回查询集QuerySet
对象。因为是列表,所以其结果可以使用values[索引].字段名
获取数据。注意不支持负索引 -
model名称.objects.retrieve()
:获取数据表中的所有记录,也就是返回查询集QuerySet
对象 -
model名称.objects.filter()
:返回满足条件的数据 -
model名称.objects.exclude()
:返回满足条件之外的数据 -
model名称.objects.order_by()
:返回排序后的结果
返回一个对象的过滤器
-
model名称.objects.get()
:返回一个model
类,而不是str
1.如果查询不到对应的结果, 则会抛出模型类.DoesNotExist
异常
2.如果返回多条结果,则会抛出模型类.MultipleObjectsReturned
异常 -
model名称.objects.count()
:返回满足查询结果的总条数 -
model名称.objects.aggregate()
:返回集合 -
model名称.objects.exists()
:判断返回的查询集中是否有数据,没有则False
,有则True
PS.
1.过滤器往往会和查询条件结合使用进行查询
2.一般情况用model名称.objects
,但也会指定对象,比如说polls.address.
查询集的特点
- 非立刻执行
1.创建查询集时,并不会访问数据库。而是直到模板中调用到数据时,才会进行数据库的访问。 - 缓存
1.查询集的结果会被保存下来,再次查询相同的数据时,会使用之前保存下来的结果数据
2.每个查询集都会有一个缓存空间来保存查询结果数据
3.切片和索引操作没有缓存可用,每次都会实际去执行sql语句
查询集索引
当查询集返回的是列表时,就可以使用下标的方式来获取我们想要的内容。
def AddressAPI(request):
# 获取第2、3、4项
address_values = AddressInfo.objects.all()[1:3]
# 构造上下文
context = {F'AddressInfo:{address_values}'}
return render(request, 'address.html', context)
三、查询集的查询条件
- 查询语句:
模型属性/表字段__条件运算符=值
1.查询语句由三部分组成:模型属性/表字段
+两个下划线__
+条件运算符,所以在定义属性模型/表字段时,不能定义包括多个下划线的名字
2.想要实现SQL中的where功能,可以使用过滤器filter()
、exclude()
、get()
- 条件运算符
1.exact
:判断相等
address = AddressInfo.objects.filter(id__exact = 1) # 查询id为1的地址
2.contains
:判断包含
address = AddressInfo.objects.filter(name__contains = '广') # 查询地址名称有广的地址
3.startswith/endswith
:以什么开头/结尾
4.isnull
:是否为null
address = AddressInfo.objects.filter(pid__isnull = true) # 查询pid为null的地址
5.in
:是否包含在范围内
address = AddressInfo.address.filter(id__in = [1,3]) # 查询id为1或3的地址
6.gt、gte、lt、lte
:大于、大于等于、小于、小
7.exclude
:条件以外的数据
address = AddressInfo.objects.filter(id__exclude = 1) # 查询id不为1的地址
8.year、month、day、week_day、hour、minute、second
:对日期时间类型的属性进行查询
books = BookInfo.objects.filter(pub_time__year = 2000) # 查询2000年发表的书籍
9.datetime.date
from datetime import date
books = BookInfo.objects.filter(pub_time__gt = date(2000,1,1)) # 查询2000年1月1号以后发布的书籍
10.多条件查询:或(使用|
隔开查询条件)
BookInfo.objects.filter(id__gt = 1 | pub_time__year = 2000) # 查询id大于1或发布年份是2000年的数据
11.多条件查询:与(使用,
隔开查询条件)
BookInfo.objects.filter(id__gt = 1 , pub_time__year = 2000) # 查询id大于1,并且发布年份是2000年的数据
12.链式查询:多个filter()
进行查询(其它过滤器也可以这样)
BookInfo.objects.filter(id__gt = 1).filter(pub_time__year = 2000) # 在查询id大于1的结果集上,再查询发布年份是2000年的数据
13.获取查询集的第一个元素以及最后一个元素:first()
、last()
BookInfo.objects.filter(id__gt = 1).first()
BookInfo.objects.filter(id__gt = 1).last()
14.排序order_by('字段/-字段')
BookInfo.objects.filter(id__gt = 1).order_by('name') # 结果按照name排序,ascall码从小到大
BookInfo.objects.filter(id__gt = 1).order_by('-name') # 结果按照name排序,ascall码从大到小
BookInfo.objects.filter(id__gt = 1).order_by('id', 'name') # 结果先按照id排序,如果相同,则按照name排序
PS.
1.exact、contains、startswith、endswith
这几个运算符都区分大小写,如需不区分大小写,只需要在前面加上i
即可:iexact、icontains、istartswitch、iendswith
四、F
对象和Q
对象
-
F
对象用于两个属性进行比较,并且支持运算
1.语法:
from django.db.models import F
F("属性名/表字段")
2.例子
2.1.查询评论量大于阅读量的书籍
books = BookInfo.objects.filter(commentcount__gt = F('readcount'))
2.2.查询评论量大于阅读量2倍的书籍
books = BookInfo.objects.filter(commentcount__gt = F('readcount' * 2))
-
Q
对象类似sql语句的where
中的and
或or
1.语法
from django.db.models import Q
# |:或
Q(属性名/表字段1__条件运算符=值) | Q(属性名/表字段2__条件运算符=值) # 查询满足第一个条件或第二个条件的数据
# ,:且
Q(属性名/表字段1__条件运算符=值) , Q(属性名/表字段2__条件运算符=值) # 查询满足第一个条件和第二个条件的数据
# 组合
Q(属性名/表字段1__条件运算符=值) , Q(属性名/表字段2__条件运算符=值) | Q(属性名/表字段3__条件运算符=值) # 查询满足条件1且(条件2或条件3)的数据
2.1.或:|
books = BookInfo.objects.filter(Q(readcount__gt = 10) | Q(id__lt) = 5) # 查询阅读量大于10或id小于5的书籍
2.2.且:,
books = BookInfo.objects.filter(Q(readcount__gt = 10) , Q(id__lt) = 5) # 查询阅读量大于10且id小于5的书籍
2.3.不等于/取反:~
books = BookInfo.objects.filter(~Q(readcount__gt = 10) , Q(id__lt) = 5) # 查询阅读量小于等于10且id小于5的书籍
2.4.组合
略
PS.如果有其它关键字条件,Q
对象需要在关键字条件后面
books = BookInfo.objects.filter(pub_time = 2000 , Q(id__lt) = 5) # pub_time是关键字
关于Q
对象更详细看文章:https://www.cnblogs.com/huchong/p/8027962.html
五、聚合函数
-
aggregate()
过滤器调用聚合函数,然后返回单个对象 - 聚合函数:
Avg('属性名/表字段')
、Max('属性名/表字段')
、Min('属性名/表字段')
、Sum('属性名/表字段')
、Count('属性名/表字段')
。使用Count('属性名/表字段')
时,一般情况下是直接调用,不需要使用aggregate()
函数 - 聚合函数在
django.db.models
导入 - 例子(注意上下文字典中,总阅读量的key的书写规则)
from django.db.models import Sum
def books(request):
# 统计所有书籍的总阅读量
readcount = BookInfo.objects.aggregate(Sum('readcount'))
# 构造上下文
context = {'readcount': readcount}
return render(request, 'Book/book.html', context)
六、关联查询(objects
前面用的是哪个class,返回的就是哪个class对象)
一对多、多对多关联查询(基础关联)
- 查询方老师所教的所有课程
# 先查询方老师
teacher = Teacher.objects.get(nickname = '方老师')
# 再通过方老师查询所有相关的任务信息
classInfo = teacher.classinfo_set.all()
- 查询python课程的老师
# 先查询课程
class = ClassInfo.object.get(title = 'python课程')
# 再通过关联查询对应的老师
teacher = class.teacher
内连接查询
- 语法:
外键字段名__从表字段名__条件
或关联模型类名小写__属性名__运算符=值
,结果和sql中的inner join
相同(内连接) - 查询书名为"红楼梦"的所有人物信息(peopleInfo)
通过书找关联的人
# 原始内连接sql语句:
select p.name, b.name from peopleinfo as p inner join bookinfo as b on p.book_id = b.id where b.name = "红楼梦";
对应语句:
peopleInfos = PeopleInfo.objects.filter(book__name='红楼梦')
- 查询书籍中人物的描述包含"红"的书籍
通过人找关联的书
bookInfos = BookInfo.books.filter(peopleinfo__description__contains='红')
自关联查询
自关联的表结构:对于地区信息、分类信息等数据,表结构非常类似,每个表的数据量十分有限,为了充分利用数据表的大量数据存储功能,可以设计成一张表,内部的关系字段指向本表的主键
说明:关系属性使用self指向本类,要求null和blank允许为空,因为一级数据是没有父级的
更多看之前的文章:https://www.jianshu.com/p/08c1be3dc9b2
七、LIKE语句中转义百分符号和下划线
在sql语句中,%
有特殊的作用,Django可以转义%
和_
,这样就可以和普通字符一样使用
xxx.objects.filter(headline__contains='%')
# 相当于sql:
SELECT ... WHERE headline LIKE '%\%%';
八、创建数据/生成数据表中的数据
- 创建和保存对象
#### 方法1
# 1.创建模型类对象,此时sql还未执行
one_value = Userinfo(username='hello', password='hi')
# 2.调用save方法,执行sql语句,生成数据
one_value.save()
#### 方法2
# 执行sql语句,生成数据,返回的是模型类对象
Userinfo.objects.create(username='hi', password='hello')
- 保存外键字段
保存外键字段和保存普通字段一样,只不过给外键字段赋值的时候迅速要注意类型要正确 - 保存多对多字段
需要调用add()
方法,而不是直接给属性赋值,不过不需要调用save()
方法
from .model.animal import Animal
from .model.cat import Cat
animal = Animal.objects.get(pk = 1)
cat1 = Cat.objects.create(named='xiaomi')
cat2 = Cat.objects.create(named='xiaohei')
animal.cats.add(cat1, cat2) # 保存多对多字段
八、修改数据和更新数据
- 修改数据:先获取数据,然后修改数据,再保存数据(一般用于外键/
ForeignKey
的修改)
value = Animal.objects.get(id=1)
value.namede = 'haha'
value.save()
- 更新数据:
update()
1.可以批量为QuerySet
中所有的对象进行更新操作
2.只能对普通字段和ForeignKey
字段使用
3.对ForeignKey
字段使用时,需要设置新值为想要指向的新模型实例
# 普通字段
Animal.objects.filter(id=1).update(named = 'hahaha')
# ForeignKey字段
c = Cat.objects.get(id=2) # 想要指向的新模型实例
Animal.objects.all().update(cat=c)
-
update()
方法会直接转换成一个sql语句,并立刻批量执行,并且不会运行模型的save()
方法。如果想要保存QuerySet
中的每个条目并确保每个实例的save()
方法都被调用,你不需要使用任何特殊的函数来处理。只需要迭代它们并调用save()
方法:
for item in my_queryset:
item.save()
-
update
方法可以配合F表达式。这对于批量更新同一模型中某个字段特别有用。例如增加Blog中每个Entry的pingback个数:
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
。然而,与filter
和exclude
子句中的F()
对象不同,在update
中不可以使用F()
对象进行跨表操作,只可以引用正在更新的模型的字段。如果尝试使用F()
对象引入另外一张表的字段,将抛出FieldError
异常:
# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))
九、删除数据
先获取数据,再删除数据
value = Animal.objects.get(id=1)
value.delete() # 该方法将返回被删除对象的总数量和一个字典,字典包含了每种被删除对象的类型和该类型的数量
# 支持批量删除
Animal.objects.filter(named='haha').delete()
- 注意
delete()
是唯一没有在管理器上暴露出来的方法。这是刻意设计的一个安全机制,用来防止意外地请求类似Animal.objects.delete()
的动作,导致不慎删除了所有的对象数据。如果你确实想删除所有的对象,你必须明确地请求一个完全的查询集,如:
Animal.objecets.all().delete()
PS.本文参考:
https://blog.csdn.net/kan2016/article/details/82868636/、https://www.cnblogs.com/huchong/p/8027962.html