
文字版:
django中如何查看orm操作对应的SQL语句
from django.db import connection
print(connection.queries)
在cmd中快速复制的方法:
右击cmd的边框,选择属性
在“选项”这一选项卡中,选中“快速编辑”,并确定
在cmd中,用左键选中一段文字,点击右键,内容就到了剪切板了
下边的方法都是QuerySet的方法,而且会返回新的QuerySet
filter
将满足条件的数据提取出来,返回一个新的QuerySet。
books=Book.objects.filter(id__gte=6).filter(~Q(id=7))
exclude
排除满足条件的数据,返回一个新的QuerySet
books=Book.objects.filter(id__gte=6).exclude(id=7)
annotate
给QuerySet中每个对象添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段
order_by
books=Book.objects.order_by('-id')
根据id进行逆序排序。
如果在order_by返回结果继续使用order_by,那么前一次的排序结果会丢失。
books=Book.objects.order_by('name').order_by('id')
最终会按照id进行排序,丢掉了按照name排序的结果。
另一种排序方式:在model的类中定义Meta类:
classMeta:
ordering=['name','-id']
在查询这张表的时候,就不用order_by,只要Book.objects.all(),结果就是先按照name排序,如果name相等再次按照id倒序排列。
all
获取这个ORM模型的QuerySet对象,可以迭代,每一条就是一行查询记录。
还是和objects方法有区别的,objects方法返回的是Manage对象,不能对它做遍历。
books=Book.objects.all()
forbookinbooks:
print(book.name)
如上,all方法返回的就是QuerySet对象,是可以迭代的,它的每个元素是Book类的对象。
values和values_list
values
用来指定提取数据时,提取哪些字段,默认是提取所有的字段。
values是用来指定提取哪些字段的。
values方法返回的是QuerySet,不过QuerySet中的数据类型不再是model对象,而是values中指定的字段和其值形成的字典。
示例:
books=Book.objects.values("id")
books=Book.objects.values("name",author_name=F("author__name"))
author是Book这张表中的一个外键字段,引用的是Author这张表。
通过F表达式,修改关联查询的结果的字段名,但是这个名字不能和Book本身表中的字段名重复。
如果values方法没有参数,会返回这张表的所有字段和其值,不包括外键。
values_list
同values的区别只是,返回的QuerySet中的数据不是字典,而是元组。只有字段的值,不包括字段的名字。
每个元组是一条记录的所有字段的值组成的,包括外键值。
示例
books=Book.objects.values_list("name",flat=True)
如果values_list方法中只传入了一个字段名,还是会返回一个元组。但是如果加上flat=True,就会把元组拆开,以str类型返回。
select_related
在提取某个model的数据的同时,也提前将相关联的数据提取出来,比如提取Book数据,可以使用select_related将author信息也提取出来,以后再次使用Book.author的时候,就不需要再次访问数据库了,可以减少数据库查询的次数。
只能用在一对多的关系,ForeignKey
示例:
1、使用select_relate示例:
books=Book.objects.select_related('author')
forbookinbooks:
print(book.author.name)
print(connection.queries)
这段语句的sql结果:
[{'time': '0.001', 'sql': 'SELECT "cmdb_book"."id", "cmdb_book"."name", "cmdb_book"."author_id", "author"."id", "author"."name", "author"."age", "author"."email" FROM "cmdb_book" INNER JOIN "author" ON ("cmdb_book"."author_id" = "author"."id")'}]
可以看到,使用select_related只有一个SQL语句。
2、不使用select_related的示例
books=Book.objects.all()
forbookinbooks:
print(book.author.name)
print(connection.queries)
这段代码的SQL语句:
[{'time': '0.001', 'sql': 'SELECT "cmdb_book"."id", "cmdb_book"."name", "cmdb_book"."author_id" FROM "cmdb_book"'},{'time': '0.000', 'sql': 'SELECT "author"."id", "author"."name", "author"."age", "author"."email" FROM "author" WHERE "author"."id" = 1'}, {'time': '0.000','sql': 'SELECT "author"."id", "author"."name", "author"."age", "author"."email" FROM "author" WHERE "author"."id" = 1'}, {'time': '0.000', 'sql': 'SELECT "author"."id", "author"."name", "author"."age", "author"."email" FROM "author" WHERE "author"."id" = 1'}, {'time': '0.000', 'sql': 'SELECT "author"."id", "author"."name", "author"."age", "author"."email" FROM "author" WHERE "author"."id" = 1'}, {'time': '0.000','sql': 'SELECT "author"."id", "author"."name", "author"."age", "author"."email" FROM "author" WHERE "author"."id" =1'}, {'time': '0.000', 'sql': 'SELECT "author"."id", "author"."name", "author"."age", "author"."email" FROM "author" WHERE "author"."id" = 1'}]
没有使用select_related时,每次有外键查询的时候,都会重新查询一次,因为我这张表里边有5条数据,每条数据的外键字段都是一条新的SQL语句。
defer
在一些表中,可能存在很多字段,但是一些字段的数据量可能非常大,而此时又不需要,比如我们在获取文章列表的时候,文章内容我们是不需要的,这时我们就可以使用defer来过滤掉一些字段。
它和value有些类似,不过value返回的QuerySet中是字典;而defer返回的QuerySet中是model对象。
示例:
books=Book.objects.defer('author')
forbookinbooks:
print(book.id,book.author)
print(connection.queries)
我们defer方法中,添加了author,那么返回的查询结果books是QuerySet,一条记录是一个model对象,但是每条记录中都没有author这个字段。
即使我们使用了defer来排除掉author,但是我们下边还是可以用book.author来获取这个字段,每一条记录都会触发一条SQL语句。
所以,如果我们确保后边的操作不会用到某个字段,才可以把这个字段defer掉。
only
跟defer相反:defer是排除掉某个字段;only是只提取这个字段。
返回的和defer一样类型。
id字段是一定会返回的,即使没有在把这个字段当做参数传进only方法中。
get
返回的不是QuerySet对象,返回的是符合条件的model对象。
给get指定的条件匹配有且仅有一条结果,如果有多个、或者没有满足,会出错。
books=Book.objects.get(id__gte=9)
这个查询,就会返回多条结果,导致程序出错。
create
创建一条数据,并且保存在数据库中。
这个方法相当于先用指定的model创建一个对象,然后调用这个对象的save方法。
get_or_create
count
在SQL层面,调用count,获取提取到的数据的条数。效率非常高。
不要使用len,len会把所有的数据从磁盘上加载到内存中,然后一个一个计算这个QuerySet中有多少条数据。效率非常低效
first和last
分别返回QuerySet中第一条和最后一条数据
books=Book.objects.all().first()
返回的是Book对象。
exists
判断符合某个条件的数据是否存在。
res=Book.objects.filter(name="宋宴").exists()
返回的是True或者False
是QuerySet可以调用的,数据记录对象没有这个方法
update
执行更新操作,在SQL底层走的也是update命令。update可能会更改多条记录,但是只需要一条SQL语句。
Book.objects.filter(name="宋宴").update(name='大宋')
上边代码更新了一条数据,成功返回1,失败返回0。
delete
Book.objects.filter(name="红楼梦").delete()
删除匹配到的QuerySet中所有记录,删除的时候要注意on_delete指定的处理方式。
成功返回1,失败返回0
distinct
去除掉重复的数据,如果底层使用的是MySQL,那么不能传递任何参数。
Author.objects.filter(name='曹雪芹').distinct()
上边的代码会筛选出所有name=曹雪芹的记录,但是每条记录的id不一样,所以不是重复的数据不会被删除。
这和SQL中有些不一样,在SQL中,我可以只筛选出某个字段,对该字段进行去重。
切片
在SQL层面实现
有时查找数据,只需要一部分,就可以用切片。
切片操作并不会把所有的数据都取出来,而是在数据库层面使用LIMIT和OFFSET来帮我们完成的。
不能在Manager类上使用,Manager的所有方法都是从QuerySet类中拷贝过来的,除了切片。因此不能用Author.objects.[1:3]之类操作。
但是可以这样使用:
objects.get_queryset()[1:2]
objects.all()[1:2]
也就是说,只要是QuerySet都可以使用切片操作,但是切片之后就不能使用filter方法了。
Django什么时候会将QuerySet转换为SQL去执行
生成一个QuerySet对象并不会马上转换为SQL语句执行。例如 :res=Book.objects.all()print(connection.queries)
把QuerySet转换为SQL的情况:
遍历
使用步长做切片。
一般的切片,并不会转化为SQL。
但是如果在切片的时候指定了步长,就会立即转化为SQL了。
Book.objects.all()[0:3:2] 指定了步长为2。
但是为什么指定了步长就会执行SQL语句呢???
使用len函数
调用len函数来查看QuerySet中总共有多少个对象,所以也需要执行SQL语句查看有多少条数据。
res=Book.objects.all()
length=len(res)
使用list函数
list函数把一个QuerySet对象转换为list对象。
判断:
如果把某个QuerySet对象放在if条件中,也会立马执行SQL语句。
网友评论