1. 执行原始SQL语句
Django提供了两种执行原始SQL语句的方法 :第一种是使用Manage.row()
方法,但是使用Manage.row()
方法只能使用原生的SQL查询不能执行删除、更新、增加操作,第二种是通过django.db.connection
连接数据库。再调用 connection.cursor()
获取光标对象。然后,调用 cursor.execute(sql, [params])
执行SQL以及执行 cursor.fetchone()
或 cursor.fetchall()
返回结果。
1.1 Manage.row()执行原生SQL查询
使用Manage.row()
方法执行原始SQL查询,返回结果是 django.db.models.query.RawQuerySet
对象实例。这个 RawQuerySet
对象实例可以像普通的 QuerySet
一样。
举一个例子来说明。假设你有以下模型:
class Person(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
birth_date = models.DateField(max_length=20)
然后可以执行自定义SQL,如下所示:
>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
... print(p)python
John Smith
Jane Jones
当然,这个操作其实和Person.objects.all()
是完全一样 , 但是, raw()
方法有很多其他的功能选择,使它变得非常强大。
1.1.1 索引查找
raw()
支持索引操作,所有如果只需要第一个查询结果,则可以这样编写代码:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
但是,索引和切片不是在数据库级别执行的。如果你有大量的 Person
对象,在SQL
级别限制查询更加有效:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]
1.1.2 惰性模型字段
raw()
方法查询结果的字段是可以省略的:
>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')
这个 Person
此查询返回的对象将是惰性的模型实例,这意味着查询中省略的字段将按需加载。例如::
>>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
... print(p.first_name,
... p.last_name)
...
John Smith
Jane Jones
从表面上看,这个查询只查询到了id
和first_name
。但是,实际上发出了3个查询。只有名字是由raw()查询检索的——最后两个名字在打印时都是按需检索的。
只有一个字段是不能忽略的-主键字段。Django使用主键来标识模型实例,因此它必须始终包含在原始查询中。安 InvalidQuery
如果忘记包含主键,将引发异常。
1.1.3 支持格式化传参
如果需要执行参数化查询,可以使用params
参数个raw()
方法传递参数
lname = 'Doe'
Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
params
可以是参数列表或字典。你可以使用 %s
列表查询字符串中的占位符,或 %(key)s
字典的占位符。
1.2 直接执行自定义SQL
直接执行自定义SQL是通过django.db.connection
连接数据库。再调用 connection.cursor()
获取光标对象。然后,调用 cursor.execute(sql, [params])
执行SQL以及执行 cursor.fetchone()
或 cursor.fetchall()
返回结果。它的DB操作和Python DB API
下规范下cursor
对象常用接口是一样的(类似于pymysql
操作mysql
数据库),它比raw()
更加强大,可以执行任意原生SQL语句。
# 使用django封装好的connection对象,会自动读取settings.py中数据库的配置信息
from django.db import connection
# 获取游标对象
cursor = connection.cursor()
# 拿到游标对象后执行sql语句
cursor.execute("select * from book")
# 获取所有的数据
rows = cursor.fetchall()
# 遍历查询到的数据
for row in rows:
print(row)
2. 模型类的增删改查操作
2.1添加数据
模型类添加数据到数据库只需要实例化模型类对象,在实例化的过程中传递需要参数(需要添加的数据值),然后在实例化模型类之后,再调用模型的save
方法,这样Django会自动的将这个模型转换成sql语句,然后存储到数据库中。示例代码如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
book = Book(name='三国演义',desc='三国英雄!')
book.save()
在没有调用save
方法之前,后台是不会执行insert
(插入)SQL语句, save()
方法是没有返回值的
Django
提供了一个更加方便的方法create()
方法,使用create()
方法添加数据可以省略save
步骤(实质上create()
方法源码里帮我们调用了save
方法):
book = Book.objects.create(name='三国演义',desc='三国英雄!')
objects
属性,Model.objects
实际上是一个 Manager
对象实例, Django
规定在模型类中至少有一个默认值 Manager
,如果你不添加自己的 Manager
,django将添加一个属性objects
包含默认值Manager
实例。如果你添加你自己的Manager
实例属性,不显示默认属性。 Manager
对象实例主要用于模型类的查询操作,比如我们修改默认属性为book_obj
(实际开发一般不修改):
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
book_obj = models.Manager()
此时使用create()
方法添加数据就变成了:
# 需要使用自定义的Manager属性
book = Book.book_obj.create(name='三国演义',desc='三国英雄!')
2.1.1 ForeignKey 和 ManyToManyField模型添加数据
更新一个 ForeignKey
字段的工作方式与保存普通字段完全相同——只需将正确类型的对象分配给相关字段即可。
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
以上使用ForeignKey
来定义模型之间的关系。即在article
的实例中可以通过author
属性来操作对应的User
模型。这样使用起来非常的方便。示例代码如下:
article = Article(title='abc',content='123')
author = User(username='张三',password='111111')
article.author = author
article.save()
# 修改article.author上的值
article.author.username = '李四'
article.save()
更新一个ManyToManyField
工作方式有点不同——使用 add()
方法来向关系中添加记录:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField("Tag",related_name="articles")
class Tag(models.Model):
name = models.CharField(max_length=50)
以上使用ManyToManyField
来定义模型之间的关系:
tag = Tag.objects.filter(name='娱乐').first()
article = Article(title='python入门',content='xxxxxxxxxxxxxxxxxx....')
article.tags.add(tag)
article
必须是模型类对象,不能是QuerySet
对象,被添加的对象(tag
)也必须是模型类对象。
2.2 查询数据
Django
使用Manager
对象实例来执行模型类的查询操作,模型类中至少有一个默认值 Manager,模型类名.objects
就是模型类默认的Manager
对象实例, 查询一般就是使用filter
、exclude
以及get
三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM
层面,这些查询条件都是使用field
+__
+condition
的方式来使用的。
查找所有数据:
要查找Book这个模型对应的表下的所有数据。那么示例代码如下:
books = Book.objects.all()
以上将返回Book模型下的所有数据。
数据过滤:
在查找数据的时候,有时候需要对一些数据进行过滤。那么这时候需要调用objects
的filter
方法。实例代码如下:
books = Book.objects.filter(name='三国演义')
> [<Book:三国演义>]
# 多个条件
books = Book.objects.filter(name='三国演义',desc='test')
调用filter
,会将所有满足条件的模型对象都返回。
获取单个对象:
使用filter
返回的是所有满足条件的结果集。有时候如果只需要返回第一个满足条件的对象。那么可以使用get
方法。示例代码如下:
book = Book.objects.get(name='三国演义')
> <Book:三国演义>
当然,如果没有找到满足条件的对象,那么就会抛出一个异常。而filter
在没有找到满足条件的数据的时候,是返回一个空的列表。
数据排序:
在之前的例子中,数据都是无序的。如果你想在查找数据的时候使用某个字段来进行排序,那么可以使用order_by
方法来实现。示例代码如下:
books = Book.objects.order_by("pub_date")
以上代码在提取所有书籍的数据的时候,将会使用pub_date
从小到大进行排序。如果想要进行倒序排序,那么可以在pub_date
前面加一个负号。实例代码如下:
books = Book.objects.order_by("-pub_date")
2.2.1 查询条件
字段查找就是如何指定SQL
的主要部分的WHERE
条件。下面是常用的查询条件的使用:
-
exact
:使用精确的=
进行查找。如果提供的是一个None
,那么在SQL
层面就是被解释为NULL
。示例代码如下:
article = Article.objects.get(id__exact=14)
article = Article.objects.get(id__exact=None)
以上的两个查找在翻译为SQL
语句为如下:
select ... from article where id=14;
select ... from article where id IS NULL;
-
iexact
:使用like
进行查找。示例代码如下:
article = Article.objects.filter(title__iexact='hello world')
那么以上的查询就等价于以下的SQL
语句:
select ... from article where title like 'hello world';
注意上面这个sql
语句,因为在MySQL
中,没有一个叫做ilike
的。所以exact
和iexact
的区别实际上就是 LIKE
和=
的区别,在大部分collation=utf8_general_ci
情况下都是一样的(collation
是用来对字符串比较的)。
-
contains
:大小写敏感,判断某个字段是否包含了某个数据。示例代码如下:
articles = Article.objects.filter(title__contains='hello')
在翻译成SQL
语句为如下:
select ... where title like binary '%hello%';
要注意的是,在使用contains
的时候,翻译成的sql
语句左右两边是有百分号的,意味着使用的是模糊查询。 而exact
翻译成sql
语句左右两边是没有百分号的,意味着使用的是精确的查询。
-
icontains
:大小写不敏感的匹配查询。示例代码如下:
articles = Article.objects.filter(title__icontains='hello')
在翻译成SQL
语句为如下:
select ... where title like '%hello%';
-
in
:提取那些给定的field
的值是否在给定的容器中。容器可以为list
、tuple
或者任何一个可以迭代的对象,包括QuerySet
对象。示例代码如下:
articles = Article.objects.filter(id__in=[1,2,3])
以上代码在翻译成SQL
语句为如下:
select ... where id in (1,3,4)
当然也可以传递一个QuerySet
对象进去。示例代码如下:
inner_qs = Article.objects.filter(title__contains='hello')
categories = Category.objects.filter(article__in=inner_qs)
以上代码的意思是获取那些文章标题包含hello
的所有分类。
将翻译成以下SQL
语句,示例代码如下:
select ...from category where article.id in (select id from article where title like '%hello%');
-
gt
:某个field
的值要大于给定的值。示例代码如下:
articles = Article.objects.filter(id__gt=4)
以上代码的意思是将所有id
大于4的文章全部都找出来。
将翻译成以下SQL
语句:
select ... where id > 4;
gte
:类似于gt
,是大于等于。lt
:类似于gt
是小于。lte
:类似于lt
,是小于等于。startswith
:判断某个字段的值是否是以某个值开始的。大小写敏感。示例代码如下:
articles = Article.objects.filter(title__startswith='hello')
以上代码的意思是提取所有标题以hello
字符串开头的文章。
将翻译成以下SQL
语句:
select ... where title like 'hello%'
istartswith
:类似于startswith
,但是大小写是不敏感的。endswith
:判断某个字段的值是否以某个值结束。大小写敏感。示例代码如下:
articles = Article.objects.filter(title__endswith='world')
以上代码的意思是提取所有标题以world
结尾的文章。
将翻译成以下SQL
语句:
select ... where title like '%world';
iendswith
:类似于endswith
,只不过大小写不敏感。range
:判断某个field
的值是否在给定的区间中。示例代码如下:
from django.utils.timezone import make_aware
from datetime import datetime
start_date = make_aware(datetime(year=2018,month=1,day=1))
end_date = make_aware(datetime(year=2018,month=3,day=29,hour=16))
articles = Article.objects.filter(pub_date__range=(start_date,end_date))
以上代码的意思是提取所有发布时间在2018/1/1
到2018/12/12
之间的文章。
将翻译成以下的SQL
语句:
select ... from article where pub_time between '2018-01-01' and '2018-12-12'。
需要注意的是,以上提取数据,不会包含最后一个值。也就是不会包含2018/12/12
的文章。
而且另外一个重点,因为我们在settings.py
中指定了USE_TZ=True
,并且设置了TIME_ZONE='Asia/Shanghai'
,因此我们在提取数据的时候要使用django.utils.timezone.make_aware
先将datetime.datetime
从navie
时间转换为aware
时间。make_aware
会将指定的时间转换为TIME_ZONE
中指定的时区的时间。
-
date
:针对某些date
或者datetime
类型的字段。可以指定date
的范围。并且这个时间过滤,还可以使用链式调用。示例代码如下:
articles = Article.objects.filter(pub_date__date=date(2018,3,29))
以上代码的意思是查找时间为2018/3/29
这一天发表的所有文章。
将翻译成以下的sql
语句:
select ... WHERE DATE(CONVERT_TZ(`front_article`.`pub_date`, 'UTC', 'Asia/Shanghai')) = 2018-03-29
注意,因为默认情况下MySQL
的表中是没有存储时区相关的信息的。因此我们需要下载一些时区表的文件,然后添加到Mysql
的配置路径中。如果你用的是windows
操作系统。那么在http://dev.mysql.com/downloads/timezones.html
下载timezone_2018d_posix.zip - POSIX standard
。然后将下载下来的所有文件拷贝到C:\ProgramData\MySQL\MySQL Server 5.7\Data\mysql
中,如果提示文件名重复,那么选择覆盖即可。
如果用的是linux
或者mac
系统,那么在命令行中执行以下命令:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p
,然后输入密码,从系统中加载时区文件更新到mysql
中。
-
year
:根据年份进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__year=2018)
articles = Article.objects.filter(pub_date__year__gte=2017)
以上的代码在翻译成SQL
语句为如下:
select ... where pub_date between '2018-01-01' and '2018-12-31';
select ... where pub_date >= '2017-01-01';
month
:同year
,根据月份进行查找。day
:同year
,根据日期进行查找。week_day
:同year
,根据星期几进行查找。1表示星期天,7表示星期六,2-6
代表的是星期一到星期五。time
:根据时间进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__time=datetime.time(12,12,12));
以上的代码是获取每一天中12点12分12秒发表的所有文章。
更多的关于时间的过滤,请参考Django官方文档 。
-
isnull
:根据值是否为空进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__isnull=False)
以上的代码的意思是获取所有发布日期不为空的文章。
将来翻译成SQL
语句如下:
select ... where pub_date is not null;
-
regex
和iregex
:大小写敏感和大小写不敏感的正则表达式。示例代码如下:
articles = Article.objects.filter(title__regex=r'^hello')
以上代码的意思是提取所有标题以hello
字符串开头的文章。
将翻译成以下的SQL
语句:
select ... where title regexp binary '^hello';
iregex
是大小写不敏感的。
根据关联的表进行查询:
假如现在有两个ORM
模型,一个是Article
,一个是Category
。代码如下:
class Category(models.Model):
"""文章分类表"""
name = models.CharField(max_length=100)
class Article(models.Model):
"""文章表"""
title = models.CharField(max_length=100,null=True)
category = models.ForeignKey("Category",on_delete=models.CASCADE)
比如想要获取文章标题中包含"hello
"的所有的分类。那么可以通过以下代码来实现:
categories = Category.object.filter(article__title__contains("hello"))
2.2.2 聚合函数
如果你用原生SQL,则可以使用聚合函数来提取数据。比如提取某个商品销售的数量,那么可以使用Count
,如果想要知道商品销售的平均价格,那么可以使用Avg
。
聚合函数是通过aggregate
方法来实现的。在讲解这些聚合函数的用法的时候,都是基于以下的模型对象来实现的。
from django.db import models
class Author(models.Model):
"""作者模型"""
name = models.CharField(max_length=100)
age = models.IntegerField()
email = models.EmailField()
class Meta:
db_table = 'author'
class Publisher(models.Model):
"""出版社模型"""
name = models.CharField(max_length=300)
class Meta:
db_table = 'publisher'
class Book(models.Model):
"""图书模型"""
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.FloatField()
rating = models.FloatField()
author = models.ForeignKey(Author,on_delete=models.CASCADE)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
class Meta:
db_table = 'book'
class BookOrder(models.Model):
"""图书订单模型"""
book = models.ForeignKey("Book",on_delete=models.CASCADE)
price = models.FloatField()
class Meta:
db_table = 'book_order'
-
Avg
:求平均值。比如想要获取所有图书的价格平均值。那么可以使用以下代码实现。from django.db.models import Avg result = Book.objects.aggregate(Avg('price')) print(result)
以上的打印结果是:
{"price__avg":23.0}
其中
price__avg
的结构是根据field__avg
规则构成的。如果想要修改默认的名字,那么可以将Avg
赋值给一个关键字参数。示例代码如下:from django.db.models import Avg result = Book.objects.aggregate(my_avg=Avg('price')) print(result)
那么以上的结果打印为:
{"my_avg":23}
-
Count
:获取指定的对象的个数。示例代码如下:from django.db.models import Count result = Book.objects.aggregate(book_num=Count('id'))
以上的
result
将返回Book
表中总共有多少本图书。
Count
类中,还有另外一个参数叫做distinct
,默认是等于False
,如果是等于True
,那么将去掉那些重复的值。比如要获取作者表中所有的不重复的邮箱总共有多少个,那么可以通过以下代码来实现:from djang.db.models import Count result = Author.objects.aggregate(count=Count('email',distinct=True))
-
Max
和Min
:获取指定对象的最大值和最小值。比如想要获取Author
表中,最大的年龄和最小的年龄分别是多少。那么可以通过以下代码来实现:from django.db.models import Max,Min result = Author.objects.aggregate(Max('age'),Min('age'))
如果最大的年龄是88,最小的年龄是18。那么以上的result将为:
{"age__max":88,"age__min":18}
-
Sum
:求指定对象的总和。比如要求图书的销售总额。那么可以使用以下代码实现:from djang.db.models import Sum result = Book.objects.annotate(total=Sum("bookstore__price")).values("name","total")
以上的代码
annotate
的意思是给Book
表在查询的时候添加一个字段叫做total
,这个字段的数据来源是从BookStore
模型的price
的总和而来。values
方法是只提取name
和total
两个字段的值。 Variance
:求指定对象的方差。StdDev
:求指定对象的标准差。
2.2.3 F表达式和Q表达式
F表达式
F表达式是用来优化ORM操作数据库的。比如我们要将公司所有员工的薪水都增加1000元,如果按照正常的流程,应该是先从数据库中提取所有的员工工资到Python内存中,然后使用Python代码在员工工资的基础之上增加1000元,最后再保存到数据库中。这里面涉及的流程就是,首先从数据库中提取数据到Python内存中,然后在Python内存中做完运算,之后再保存到数据库中。示例代码如下:
employees = Employee.objects.all()
for employee in employees:
employee.salary += 1000
employee.save()
而我们的F表达式就可以优化这个流程,他可以不需要先把数据从数据库中提取出来,计算完成后再保存回去,他可以直接执行SQL语句,就将员工的工资增加1000元。示例代码如下:
from djang.db.models import F
Employee.object.update(salary=F("salary")+1000)
F表达式并不会马上从数据库中获取数据,而是在生成SQL语句的时候,动态的获取传给F表达式的值。
比如如果想要获取作者中,name
和email
相同的作者数据。如果不使用F表达式,那么需要使用以下代码来完成:
authors = Author.objects.all()
for author in authors:
if author.name == author.email:
print(author)
如果使用F表达式,那么一行代码就可以搞定。示例代码如下:
from django.db.models import F
authors = Author.objects.filter(name=F("email"))
Q表达式
如果想要实现所有价格高于100元,并且评分达到9.0以上评分的图书。那么可以通过以下代码来实现:
books = Book.objects.filter(price__gte=100,rating__gte=9)
以上这个案例是一个并集查询,可以简单的通过传递多个条件进去来实现。
但是如果想要实现一些复杂的查询语句,比如要查询所有价格低于10元,或者是评分低于9分的图书。那就没有办法通过传递多个条件进去实现了。这时候就需要使用Q表达式来实现了。示例代码如下:
from django.db.models import Q
books = Book.objects.filter(Q(price__lte=10) | Q(rating__lte=9))
以上是进行或运算,当然还可以进行其他的运算,比如有&
和~
(非)等。一些用Q表达式的例子如下:
from django.db.models import Q
# 获取id等于3的图书
books = Book.objects.filter(Q(id=3))
# 获取id等于3,或者名字中包含文字"记"的图书
books = Book.objects.filter(Q(id=3)|Q(name__contains("记")))
# 获取价格大于100,并且书名中包含"记"的图书
books = Book.objects.filter(Q(price__gte=100)&Q(name__contains("记")))
# 获取书名包含“记”,但是id不等于3的图书
books = Book.objects.filter(Q(name__contains='记') & ~Q(id=3))
2.3 QuerySet API
我们通常做查询操作的时候,都是通过模型名字.objects
的方式进行操作。其实模型名字.objects
是一个django.db.models.manager.Manager
对象,而Manager
这个类是一个“空壳”的类,他本身是没有任何的属性和方法的。他的方法全部都是通过Python
动态添加的方式,从QuerySet
类中拷贝过来的。示例图如下:
所以我们如果想要学习
ORM
模型的查找操作,必须首先要学会QuerySet
上的一些API
的使用。
2.3.1 返回新的QuerySet
的方法
在使用QuerySet
进行查找操作的时候,可以提供多种操作。比如过滤完后还要根据某个字段进行排序,那么这一系列的操作我们可以通过一个非常流畅的链式调用
的方式进行。比如要从文章表中获取标题为123
,并且提取后要将结果根据发布的时间进行排序,那么可以使用以下方式来完成:
articles = Article.objects.filter(title='123').order_by('create_time')
可以看到order_by
方法是直接在filter
执行后调用的。这说明filter
返回的对象是一个拥有order_by
方法的对象。而这个对象正是一个新的QuerySet
对象。因此可以使用order_by
方法。
那么以下将介绍在那些会返回新的QuerySet
对象的方法。
filter
:将满足条件的数据提取出来,返回一个新的QuerySet
。具体的filter
可以提供什么条件查询。请见查询操作章节。-
exclude
:排除满足条件的数据,返回一个新的QuerySet
。示例代码如下:Article.objects.exclude(title__contains='hello')
以上代码的意思是提取那些标题不包含
hello
的图书。 -
annotate
:给QuerySet
中的每个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段。示例代码如下:articles = Article.objects.annotate(author_name=F("author__name"))
以上代码将在每个对象中都添加一个
author__name
的字段,用来显示这个文章的作者的年龄。 -
order_by
:指定将查询的结果根据某个字段进行排序。如果要倒叙排序,那么可以在这个字段的前面加一个负号。示例代码如下:# 根据创建的时间正序排序 articles = Article.objects.order_by("create_time") # 根据创建的时间倒序排序 articles = Article.objects.order_by("-create_time") # 根据作者的名字进行排序 articles = Article.objects.order_by("author__name") # 首先根据创建的时间进行排序,如果时间相同,则根据作者的名字进行排序 articles = Article.objects.order_by("create_time",'author__name')
一定要注意的一点是,多个
order_by
,会把前面排序的规则给打乱,而使用后面的排序方式。比如以下代码:articles = Article.objects.order_by("create_time").order_by("author__name")
他会根据作者的名字进行排序,而不是使用文章的创建时间。
-
values
:用来指定在提取数据出来,需要提取哪些字段。默认情况下会把表中所有的字段全部都提取出来,可以使用values
来进行指定,并且使用了values
方法后,提取出的QuerySet
中的数据类型不是模型,而是在values
方法中指定的字段和值形成的字典:articles = Article.objects.values("title",'content') for article in articles: print(article)
以上打印出来的
article
是类似于{"title":"abc","content":"xxx"}
的形式。
如果在values
中没有传递任何参数,那么将会返回这个恶模型中所有的属性。 -
values_list
:类似于values
。只不过返回的QuerySet
中,存储的不是字典,而是元组。示例代码如下:articles = Article.objects.values_list("id","title") print(articles)
那么在打印
articles
后,结果为``等。
如果在values_list
中只有一个字段。那么你可以传递flat=True
来将结果扁平化。示例代码如下:articles1 = Article.objects.values_list("title") >> <QuerySet [("abc",),("xxx",),...]> articles2 = Article.objects.values_list("title",flat=True) >> <QuerySet ["abc",'xxx',...]>
all
:获取这个ORM
模型的QuerySet
对象。-
select_related
:在提取某个模型的数据的同时,也提前将相关联的数据提取出来。比如提取文章数据,可以使用select_related
将author
信息提取出来,以后再次使用article.author
的时候就不需要再次去访问数据库了。可以减少数据库查询的次数。示例代码如下:article = Article.objects.get(pk=1) >> article.author # 重新执行一次查询语句 article = Article.objects.select_related("author").get(pk=2) >> article.author # 不需要重新执行查询语句了
selected_related
只能用在一对多
或者一对一
中,不能用在多对多
或者多对一
中。比如可以提前获取文章的作者,但是不能通过作者获取这个作者的文章,或者是通过某篇文章获取这个文章所有的标签。 -
prefetch_related
:这个方法和select_related
非常的类似,就是在访问多个表中的数据的时候,减少查询的次数。这个方法是为了解决多对一
和多对多
的关系的查询问题。比如要获取标题中带有hello
字符串的文章以及他的所有标签,示例代码如下:from django.db import connection articles = Article.objects.prefetch_related("tag_set").filter(title__contains='hello') print(articles.query) # 通过这条命令查看在底层的SQL语句 for article in articles: print("title:",article.title) print(article.tag_set.all()) # 通过以下代码可以看出以上代码执行的sql语句 for sql in connection.queries: print(sql)
但是如果在使用
article.tag_set
的时候,如果又创建了一个新的QuerySet
那么会把之前的SQL
优化给破坏掉。比如以下代码:tags = Tag.obejcts.prefetch_related("articles") for tag in tags: articles = tag.articles.filter(title__contains='hello') #因为filter方法会重新生成一个QuerySet,因此会破坏掉之前的sql优化 # 通过以下代码,我们可以看到在使用了filter的,他的sql查询会更多,而没有使用filter的,只有两次sql查询 for sql in connection.queries: print(sql)
那如果确实是想要在查询的时候指定过滤条件该如何做呢,这时候我们可以使用
django.db.models.Prefetch
来实现,Prefetch
这个可以提前定义好queryset
。示例代码如下:tags = Tag.objects.prefetch_related(Prefetch("articles",queryset=Article.objects.filter(title__contains='hello'))).all() for tag in tags: articles = tag.articles.all() for article in articles: print(article) for sql in connection.queries: print('='*30) print(sql)
因为使用了
Prefetch
,即使在查询文章的时候使用了filter
,也只会发生两次查询操作。 -
defer
:在一些表中,可能存在很多的字段,但是一些字段的数据量可能是比较庞大的,而此时你又不需要,比如我们在获取文章列表的时候,文章的内容我们是不需要的,因此这时候我们就可以使用defer
来过滤掉一些字段。这个字段跟values
有点类似,只不过defer
返回的不是字典,而是模型。示例代码如下:articles = list(Article.objects.defer("title")) for sql in connection.queries: print('='*30) print(sql)
在看以上代码的
sql
语句,你就可以看到,查找文章的字段,除了title
,其他字段都查找出来了。当然,你也可以使用article.title
来获取这个文章的标题,但是会重新执行一个查询的语句。示例代码如下:articles = list(Article.objects.defer("title")) for article in articles: # 因为在上面提取的时候过滤了title # 这个地方重新获取title,将重新向数据库中进行一次查找操作 print(article.title) for sql in connection.queries: print('='*30) print(sql)
defer
虽然能过滤字段,但是有些字段是不能过滤的,比如id
,即使你过滤了,也会提取出来。 only
:跟defer
类似,只不过defer
是过滤掉指定的字段,而only
是只提取指定的字段。get
:获取满足条件的数据。这个函数只能返回一条数据,并且如果给的条件有多条数据,那么这个方法会抛出MultipleObjectsReturned
错误,如果给的条件没有任何数据,那么就会抛出DoesNotExit
错误。所以这个方法在获取数据的只能,只能有且只有一条。-
create
:创建一条数据,并且保存到数据库中。这个方法相当于先用指定的模型创建一个对象,然后再调用这个对象的save
方法。示例代码如下:article = Article(title='abc') article.save() # 下面这行代码相当于以上两行代码 article = Article.objects.create(title='abc')
-
get_or_create
:根据某个条件进行查找,如果找到了那么就返回这条数据,如果没有查找到,那么就创建一个。示例代码如下:obj,created= Category.objects.get_or_create(title='默认分类')
如果有标题等于
默认分类
的分类,那么就会查找出来,如果没有,则会创建并且存储到数据库中。
这个方法的返回值是一个元组,元组的第一个参数obj
是这个对象,第二个参数created
代表是否创建的。 -
bulk_create
:一次性创建多个数据。示例代码如下:Tag.objects.bulk_create([ Tag(name='111'), Tag(name='222'), ])
count
:获取提取的数据的个数。如果想要知道总共有多少条数据,那么建议使用count
,而不是使用len(articles)
这种。因为count
在底层是使用select count(*)
来实现的,这种方式比使用len
函数更加的高效。first
和last
:返回QuerySet
中的第一条和最后一条数据。aggregate
:使用聚合函数。-
exists
:判断某个条件的数据是否存在。如果要判断某个条件的元素是否存在,那么建议使用exists
,这比使用count
或者直接判断QuerySet
更有效得多。示例代码如下:if Article.objects.filter(title__contains='hello').exists(): print(True) 比使用count更高效: if Article.objects.filter(title__contains='hello').count() > 0: print(True) 也比直接判断QuerySet更高效: if Article.objects.filter(title__contains='hello'): print(True)
-
distinct
:去除掉那些重复的数据。这个方法如果底层数据库用的是MySQL
,那么不能传递任何的参数。比如想要提取所有销售的价格超过80元的图书,并且删掉那些重复的,那么可以使用distinct
来帮我们实现,示例代码如下:books = Book.objects.filter(bookorder__price__gte=80).distinct()
需要注意的是,如果在
distinct
之前使用了order_by
,那么因为order_by
会提取order_by
中指定的字段,因此再使用distinct
就会根据多个字段来进行唯一化,所以就不会把那些重复的数据删掉。示例代码如下:orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()
那么以上代码因为使用了
order_by
,即使使用了distinct
,也会把重复的book_id
提取出来。 -
update
:执行更新操作,在SQL
底层走的也是update
命令。比如要将所有category
为空的article
的article
字段都更新为默认的分类。示例代码如下:Article.objects.filter(category__isnull=True).update(category_id=3)
注意这个方法走的是更新的逻辑。所以更新完成后保存到数据库中不会执行
save
方法,因此不会更新auto_now
设置的字段。 delete
:删除所有满足条件的数据。删除数据的时候,要注意on_delete
指定的处理方式。-
切片操作:有时候我们查找数据,有可能只需要其中的一部分。那么这时候可以使用切片操作来帮我们完成。
QuerySet
使用切片操作就跟列表使用切片操作是一样的。示例代码如下:books = Book.objects.all()[1:3] for book in books: print(book)
切片操作并不是把所有数据从数据库中提取出来再做切片操作。而是在数据库层面使用
LIMIE
和OFFSET
来帮我们完成。所以如果只需要取其中一部分的数据的时候,建议大家使用切片操作。
2.3.1 不返回QuerySet的方法
以下方法不使用缓存,它们每次被调用时都会查询数据库。
get()
-
get
(**kwargs
)
返回与给定查找参数匹配的对象,该对象应采用中描述的格式。
get()
如果找到多个对象。就会抛出 MultipleObjectsReturned
异常,该异常是模型类的属性。
get()
如果没有为给定参数找到对象,则出现DoesNotExist
异常。此异常是模型类的属性。例子:
Entry.objects.get(id='foo') # raises Entry.DoesNotExist
这个DoesNotExist
异常继承自django.core.exceptions.ObjectDoesNotExist
,这样您可以针对多个 DoesNotExist
例外情况。例子:
from django.core.exceptions import ObjectDoesNotExist
try:
e = Entry.objects.get(id=3)
b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
print("Either the entry or blog doesn't exist.")
如果希望查询集返回一行,则可以使用 get()
不带任何参数返回该行的对象::
entry = Entry.objects.filter(...).exclude(...).get()
create()
-
create
(**kwargs
)
一种方便的方法,用于创建一个对象并将其全部保存在一个步骤中。因此:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
和:
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
是等价的。
这个 force_insert
参数在其他地方有文档记录,但这意味着总是会创建一个新对象。通常你不需要担心这个。但是,如果模型包含您设置的手动主键值,并且该值已存在于数据库中,则调用 create()
将失败 IntegrityError
因为主键必须是唯一的。如果您使用的是手动主键,请准备好处理异常。
get_or_create()
-
get_or_create
(defaults=None, **kwargs)
一种方便的查找给定对象的方法 kwargs
(如果模型具有所有字段的默认值,则可能为空),如有必要,请创建一个字段。
返回的元组 (object, created)
在哪里 object
是检索或创建的对象,并且 created
是一个布尔值,指定是否创建了新对象。
这意味着这是一个到样板文件代码的快捷方式。例如::
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()
随着模型中字段数的增加,这种模式变得相当复杂。上面的例子可以用重写 get_or_create()
像这样::
obj, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
传递给的任何关键字参数 get_or_create()
- 除了 一个可选的调用 defaults
-将用于 get()
调用。如果找到一个对象, get_or_create()
返回该对象的元组,然后 False
.
可以通过链接为检索到的对象指定更复杂的条件 get_or_create()
具有 filter()
并使用 Q objects
. 例如,要检索罗伯特或鲍勃马利(如果两者都存在),并创建后者,否则:
from django.db.models import Q
obj, created = Person.objects.filter(
Q(first_name='Bob') | Q(first_name='Robert'),
).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})
如果找到多个对象, get_or_create()
加薪 MultipleObjectsReturned
. 如果对象是 not 发现, get_or_create()
将实例化并保存新对象,返回新对象的元组 True
. 将根据以下算法大致创建新对象:
params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()
在英语中,这意味着从任何不包含双下划线(表示非精确查找)的非“默认值”关键字参数开始。然后添加 defaults
,根据需要重写任何键,并将结果用作模型类的关键字参数。如果在 defaults
,评估它们。如上所述,这是对所用算法的简化,但它包含了所有相关的细节。内部实现有比这更多的错误检查,并处理一些额外的边缘条件;如果您感兴趣,请阅读代码。
如果您有一个名为 defaults
并希望在 get_or_create()
只是使用 'defaults__exact'
,像这样::
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
这个 get_or_create()
方法的错误行为与 create()
当您使用手动指定的主键时。如果需要创建一个对象并且该键已经存在于数据库中,则 IntegrityError
将被提升。
这个方法是原子的,假定正确的用法、正确的数据库配置和底层数据库的正确行为。但是,如果在数据库级别不强制 kwargs
用于 get_or_create
调用(见) unique
或 unique_together
,该方法容易出现竞争条件,导致多行同时插入相同参数。
如果您使用的是MySQL,请确保使用 READ COMMITTED
隔离级别而不是 REPEATABLE READ
(默认),否则您可能会看到 get_or_create
将提高 IntegrityError
但是这个物体不会出现在后面 get()
打电话。
最后,一句关于使用的话 get_or_create()
在Django视图中。请确保只在 POST
除非你有充分的理由不这样做。 GET
请求不应该对数据有任何影响。相反,使用 POST
每当对页面的请求对数据有副作用时。更多,请参见 Safe methods 在HTTP规范中。
警告
你可以使用 get_or_create()
通过 ManyToManyField
属性和反向关系。在这种情况下,您将在该关系的上下文中限制查询。如果你不经常使用它,可能会导致一些完整性问题。
以下型号:
class Chapter(models.Model):
title = models.CharField(max_length=255, unique=True)
class Book(models.Model):
title = models.CharField(max_length=256)
chapters = models.ManyToManyField(Chapter)
你可以使用 get_or_create()
通过书籍的章节字段,但它只在该书的上下文中提取:
>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError
这是因为它试图通过《尤利西斯》这本书获得或创造“第1章”,但它不能做到任何一个:关系不能获取那一章,因为它与那本书无关,但也不能创造它,因为 title
字段应该是唯一的。
update_or_create()
-
update_or_create
(defaults=None, **kwargs)
用给定的 kwargs
,必要时创建新的。这个 defaults
是用于更新对象的(字段、值)对的字典。中的值 defaults
可以是可调用的。
返回的元组 (object, created)
在哪里 object
是创建或更新的对象,并且 created
是一个布尔值,指定是否创建了新对象。
这个 update_or_create
方法尝试根据给定的 kwargs
. 如果找到匹配项,它将更新 defaults
字典。
这意味着这是一个到样板文件代码的快捷方式。例如::
defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
随着模型中字段数的增加,这种模式变得相当复杂。上面的例子可以用重写 update_or_create()
像这样::
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
有关如何传入名称的详细描述 kwargs
已解决,请参阅 get_or_create()
.
如上所述 get_or_create()
,此方法容易出现竞争条件,如果在数据库级别不强制唯一性,则可能导致同时插入多行。
喜欢 get_or_create()
和 create()
,如果正在使用手动指定的主键,并且需要创建一个对象,但该键已存在于数据库中,则 IntegrityError
提高了。
bulk_create()
-
bulk_create
(objs, batch_size=None, ignore_conflicts=False)
此方法以有效的方式将提供的对象列表插入数据库(通常只有1个查询,不管有多少个对象)::
>>> Entry.objects.bulk_create([
... Entry(headline='This is a test'),
... Entry(headline='This is only a test'),
... ])
不过,这有许多警告:
模型的
save()
将不调用方法,并且pre_save
和post_save
不会发送信号。它不适用于多表继承方案中的子模型。
如果模型的主键是
AutoField
它不检索和设置主键属性,因为save()
是的,除非数据库后端支持它(目前是PostgreSQL)。它不适用于多对多关系。
-
它铸造
objs
到一个列表,该列表完全评估objs
如果是发电机。强制转换允许检查所有对象,以便可以首先插入具有手动设置的主键的任何对象。如果要批量插入对象而不同时评估整个生成器,则可以使用此技术,只要对象没有任何手动设置的主键::from itertools import islice batch_size = 100 objs = (Entry(headline='Test %s' % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
这个 batch_size
参数控制在单个查询中创建的对象数。默认值是在一个批处理中创建所有对象,但sqlite除外,其中默认值是每个查询最多使用999个变量。
在支持它的数据库(除了Oracle)上,设置 ignore_conflicts
参数到 True
告诉数据库忽略插入任何失败约束的行(如重复的唯一值)的失败。启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持)。
Changed in Django 2.2:
这个 ignore_conflicts
已添加参数。
bulk_update()
New in Django 2.2:
-
bulk_update
(objs, fields, batch_size=None)
此方法可以有效地更新所提供模型实例上的给定字段,通常只需一个查询:
>>> objs = [
... Entry.objects.create(headline='Entry 1'),
... Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])
QuerySet.update()
用于保存更改,因此这比遍历模型列表和调用 save()
但有几点需要注意:
- 无法更新模型的主键。
- 每个模型
save()
未调用方法,并且pre_save
和post_save
没有发送信号。 - 如果更新大量行中的大量列,则生成的SQL可能非常大。通过指定合适的
batch_size
. - 更新在多表继承祖先上定义的字段将对每个祖先产生额外的查询。
- 如果
objs
包含重复项,只更新第一个。
这个 batch_size
参数控制单个查询中保存的对象数。默认情况下,更新一批中的所有对象,除了对查询中使用的变量数量有限制的sqlite和oracle。
count()
-
count
()
返回一个整数,该整数表示数据库中与 QuerySet
.
例子::
# Returns the total number of entries in the database.
Entry.objects.count()
# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()
A count()
调用执行 SELECT COUNT(*)
在幕后,所以你应该经常使用 count()
而不是将所有记录加载到Python对象中并调用 len()
在结果上(除非您无论如何都需要将对象加载到内存中,在这种情况下 len()
会更快)。
请注意,如果需要 QuerySet
并且还从中检索模型实例(例如,通过对其进行迭代),使用它可能更有效 len(queryset)
这不会导致额外的数据库查询 count()
会。
in_bulk()
-
in_bulk
(id_list=None, field_name='pk')
获取字段值列表 (id_list
) field_name
对于这些值,并返回将每个值映射到具有给定字段值的对象实例的字典。如果 id_list
未提供,将返回查询集中的所有对象。 field_name
必须是唯一字段,并且默认为主键。
例子::
>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}
如果你通过 in_bulk()
一个空列表,你会得到一本空字典。
iterator()
-
iterator
(chunk_size=2000)
评估 QuerySet
(通过执行查询)并返回迭代器(请参见 PEP 234 )在结果上。一 QuerySet
通常在内部缓存其结果,以便重复的计算不会导致额外的查询。相反, iterator()
将直接读取结果,而不在 QuerySet
级别(在内部,默认迭代器调用 iterator()
并缓存返回值)。对于一个 QuerySet
它返回大量只需要访问一次的对象,这可以导致更好的性能和显著的内存减少。
注意使用 iterator()
在一 QuerySet
已评估的将强制它重新评估,重复查询。
此外,使用 iterator()
前因 prefetch_related()
调用将被忽略,因为这两个优化在一起没有意义。
根据数据库后端的不同,查询结果将同时加载,或者使用服务器端光标从数据库中进行流式传输。
使用服务器端光标
甲骨文与 PostgreSQL 使用服务器端游标从数据库流式传输结果,而不将整个结果集加载到内存中。
Oracle数据库驱动程序始终使用服务器端游标。
对于服务器端光标, chunk_size
参数指定要在数据库驱动程序级别缓存的结果数。获取更大的块会减少数据库驱动程序和数据库之间的往返次数,但会牺牲内存。
在PostgreSQL上,只有在 DISABLE_SERVER_SIDE_CURSORS
设置是 False
. 读 事务池和服务器端游标 如果您使用的是在事务池模式下配置的连接池。禁用服务器端游标时,行为与不支持服务器端游标的数据库相同。
没有服务器端光标
MySQL不支持流式结果,因此python数据库驱动程序将整个结果集加载到内存中。然后,数据库适配器使用 fetchmany()
方法定义于 PEP 249 .
sqlite可以使用 fetchmany()
但是,由于sqlite不提供连接内查询之间的隔离,因此在写入要循环访问的表时要小心。见 使用时隔离 QuerySet.iterator() 更多信息。
这个 chunk_size
参数控制从数据库驱动程序中检索的批次django的大小。更大的批处理减少了与数据库驱动程序通信的开销,但代价是内存消耗略有增加。
默认值为 chunk_size
,2000,来自 a calculation on the psycopg mailing list :
假设有10-20列的行与文本和数字数据混合,2000将获取少于100kb的数据,这似乎是在传输的行数和过早退出循环时丢弃的数据之间的一个很好的折衷。
Changed in Django 2.2:
添加了对sqlite上的结果流的支持。
latest()
-
latest
(*fields)
基于给定字段返回表中的最新对象。
此示例返回最新的 Entry
在表格上,根据 pub_date
领域:
Entry.objects.latest('pub_date')
您还可以根据几个字段选择最新的。例如,选择 Entry
最早的 expire_date
当两个条目相同时 pub_date
::
Entry.objects.latest('pub_date', '-expire_date')
负面签到 '-expire_date'
排序方法 expire_date
在里面 下降的 秩序。自从 latest()
获取最后一个结果, Entry
最早的 expire_date
被选中。
如果你的模型 Meta 指定 get_latest_by
,您可以省略 earliest()
或 latest()
. 中指定的字段 get_latest_by
默认情况下将使用。
喜欢 get()
, earliest()
和 latest()
提升 DoesNotExist
如果没有具有给定参数的对象。
注意 earliest()
和 latest()
纯粹为了方便和可读性而存在。
earliest()
和 latest()
可以返回日期为空的实例。
由于将排序委托给数据库,因此如果使用不同的数据库,允许空值的字段的结果的排序可能会有所不同。例如,PostgreSQL和MySQL对空值进行排序,就像它们高于非空值一样,而SQLite则相反。
您可能希望筛选出空值::
Entry.objects.filter(pub_date__isnull=False).latest('pub_date')
earliest()
-
earliest
(*fields)
以其他方式工作 latest()
但方向改变了。
first()
-
first
()
返回与查询集匹配的第一个对象,或者 None
如果没有匹配的对象。如果 QuerySet
未定义排序,则按主键自动排序查询集。这可能会影响聚合结果,如中所述 与默认排序或 order_by() .
例子::
p = Article.objects.order_by('title', 'pub_date').first()
注意 first()
是一种方便的方法,下面的代码示例等效于上面的示例:
try:
p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
p = None
last()
-
last
()
作品像 first()
,但返回查询集中的最后一个对象。
aggregate()
-
aggregate
(*args, **kwargs)
返回计算的聚合值(平均值、和等)的字典 QuerySet
. 每个参数 aggregate()
指定将包含在返回的字典中的值。
Django提供的聚合函数在 Aggregation Functions 下面。因为集料也是 query expressions 可以将聚合与其他聚合或值组合以创建复杂聚合。
使用关键字参数指定的聚合将使用关键字作为批注的名称。匿名参数将根据聚合函数的名称和要聚合的模型字段为其生成一个名称。复杂聚合不能使用匿名参数,必须将关键字参数指定为别名。
例如,当您处理博客条目时,您可能希望知道贡献博客条目的作者数:
>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}
通过使用关键字参数指定聚合函数,可以控制返回的聚合值的名称::
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}
有关聚合的深入讨论,请参见 the topic guide on Aggregation .
exists()
-
exists
()
返回 True
如果 QuerySet
包含任何结果,以及 False
如果不是。它试图以最简单和最快的方式执行查询,但是 does 执行与普通查询几乎相同的查询 QuerySet
查询。
exists()
对于与中的两个对象成员关系相关的搜索很有用 QuerySet
以及任何对象的存在 QuerySet
尤其是在大 QuerySet
.
找到具有唯一字段的模型(例如 primary_key
)是的成员 QuerySet
是::
entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
这将比以下更快,后者需要对整个查询集进行计算和迭代:
if entry in some_queryset:
print("Entry contained in QuerySet")
要查找查询集是否包含任何项:
if some_queryset.exists():
print("There is at least one object in some_queryset")
速度比:
if some_queryset:
print("There is at least one object in some_queryset")
…但不是很大程度上(因此需要一个大型的查询集来提高效率)。
此外,如果 some_queryset
尚未评估,但您知道它将在某个时间点,然后使用 some_queryset.exists()
将比简单地使用 bool(some_queryset)
,它检索结果,然后检查是否返回了任何结果。
update()
-
update
(**kwargs)
对指定的字段执行SQL更新查询,并返回匹配的行数(如果某些行已有新值,则可能不等于更新的行数)。
例如,要关闭2010年发布的所有日志的评论,可以执行以下操作:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
(这假设 Entry
模型有字段 pub_date
和 comments_on
)
您可以更新多个字段-数量没有限制。例如,这里我们更新 comments_on
和 headline
领域::
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
这个 update()
方法被立即应用,并且对 QuerySet
它只能更新模型主表中的列,而不能更新相关模型上的列。您不能这样做,例如:
>>> Entry.objects.update(blog__name='foo') # Won't work!
基于相关字段的筛选仍然是可能的,但是:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
你不能调用 update()
在一 QuerySet
已经取了一个切片或不能再过滤的。
这个 update()
方法返回受影响的行数::
>>> Entry.objects.filter(id=64).update(comments_on=True)
1
>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132
如果您只是更新一个记录,而不需要对模型对象做任何事情,那么最有效的方法是调用 update()
而不是将模型对象加载到内存中。例如,不要这样做:
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()
…这样做:
Entry.objects.filter(id=10).update(comments_on=False)
使用 update()
还可以防止在加载对象和调用之间的短时间内,数据库中的某些内容可能发生变化的争用情况。 save()
.
最后,要意识到 update()
在SQL级别进行更新,因此不调用任何 save()
方法,它也不会发出 pre_save
或 post_save
信号(是调用的结果 Model.save()
)如果要为具有自定义 save()
方法,循环它们并调用 save()
,像这样:
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
delete()
-
delete
()
对中的所有行执行SQL删除查询 QuerySet
并返回删除的对象数和一个字典,其中包含每个对象类型的删除数。
这个 delete()
立即应用。你不能调用 delete()
在一 QuerySet
已经取了一个切片或不能再过滤的。
例如,删除特定日志中的所有条目:
>>> b = Blog.objects.get(pk=1)
# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})
默认情况下,Django的 ForeignKey
模拟SQL约束 ON DELETE CASCADE
-换言之,任何具有指向要删除对象的外键的对象都将与它们一起删除。例如::
>>> blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})
这个级联行为可以通过 on_delete
论据 ForeignKey
.
这个 delete()
方法执行批量删除,不调用任何 delete()
模型上的方法。但是,它确实会发出 pre_delete
和 post_delete
所有已删除对象的信号(包括级联删除)。
Django需要将对象提取到内存中以发送信号并处理级联。但是,如果没有级联和信号,那么Django可能会采用快速路径,删除对象而不提取到内存中。对于较大的删除,这可能会导致内存使用显著减少。执行的查询量也可以减少。
设置为 on_delete
DO_NOTHING
不要阻止删除时采用快速路径。
注意,在对象删除中生成的查询是一个可更改的实现细节。
as_manager()
-
classmethod
as_manager
()
返回的实例的类方法 Manager
一份 QuerySet
的方法。见 使用创建经理 QuerySet 方法 了解更多详细信息。
explain()
-
explain
(format=None, **options)
返回 QuerySet
的执行计划,其中详细说明了数据库将如何执行查询,包括将要使用的任何索引或联接。了解这些详细信息可能有助于提高慢速查询的性能。
例如,使用PostgreSQL时:
>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog (cost=0.00..35.50 rows=10 width=12)
Filter: (title = 'My Blog'::bpchar)
数据库之间的输出差异很大。
explain()
所有内置数据库后端都支持,但Oracle除外,因为实现并不简单。
这个 format
参数从数据库的默认值(通常基于文本)更改输出格式。PostgreSQL支持 'TEXT'
, 'JSON'
, 'YAML'
和 'XML'
. MySQL支持 'TEXT'
(也称为 'TRADITIONAL'
) 'JSON'
.
一些数据库接受可以返回有关查询的更多信息的标志。将这些标志作为关键字参数传递。例如,使用PostgreSQL时:
>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True))
Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
Output: id, title
Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms
在某些数据库上,标记可能会导致执行查询,这可能会对数据库产生不利影响。例如,PostgreSQL的 ANALYZE
如果存在触发器或调用函数,则标志可能导致对数据的更改,即使对于 SELECT
查询。
2.3.3 QuerySet惰性执行和缓存
生成一个QuerySet
对象并不会马上转换为SQL
语句去执行。
比如我们获取Book
表下所有的图书:
books = Book.objects.all()
print(connection.queries)
我们可以看到在打印connection.quries
的时候打印的是一个空的列表。说明上面的QuerySet
并没有真正的执行。
在以下情况下QuerySet
会被转换为SQL
语句执行:
-
迭代:在遍历
QuerySet
对象的时候,会首先先执行这个SQL
语句,然后再把这个结果返回进行迭代。比如以下代码就会转换为SQL
语句:for book in Book.objects.all(): print(book)
使用步长做切片操作:
QuerySet
可以类似于列表一样做切片操作。做切片操作本身不会执行SQL
语句,但是如果如果在做切片操作的时候提供了步长,那么就会立马执行SQL
语句。需要注意的是,做切片后不能再执行filter
方法,否则会报错。调用
len
函数:调用len
函数用来获取QuerySet
中总共有多少条数据也会执行SQL
语句。调用
list
函数:调用list
函数用来将一个QuerySet
对象转换为list
对象也会立马执行SQL
语句。判断:如果对某个
QuerySet
进行判断,也会立马执行SQL
语句。
2.4 删除数据
在查找到数据后,便可以进行删除了。删除数据非常简单,只需要调用这个对象的delete
方法即可。实例代码如下:
book = Book.objects.get(name='三国演义')
book.delete()
2.5 修改数据
在查找到数据后,便可以进行修改了。修改的方式非常简单,只需要将查找出来的对象的某个属性进行修改,然后再调用这个对象的save
方法便可以进行修改。示例代码如下:
from datetime import datetime
book = Book.objects.get(name='三国演义')
book.pub_date = datetime.now()
book.save()