QuerySet 能够被构造,过滤,切片,作为参数传递,这些行为都不会对数据库进行操做。只要你查询的时候才真正的操做数据库。python
下面的 QuerySet 行为会致使执行查询的操做:正则表达式
循环(Iteration):QuerySet 是可迭代的,在你遍历对象时就会执行数据库操做。例如,打印出全部博文的大标题:算法
for e in Entry.objects.all(): print(e.headline)
切片(Slicing): QuerySet 是能够用 Python 的数组切片语法完成切片。通常来讲对一个未查询的 QuerySet 切片就返回另外一个未查询的 QuerySet (新 QuerySet 不会被执行)。不过若是你在切片时使用了 "step" 参数,Django 仍会执行数据库操做而且返回列表对象。对一个查询过的QuerySet执行切片也会返回列表对象。数据库
序列化/缓存化(Pickling/Caching): 详情请查看 pickling QuerySets。 这一节所强调的一点是查询结果是从数据库中读取的。django
repr(). 调用 QuerySet 的 repr() 方法时,查询就会被运行。这对于 Python 命令行来讲很是方便,你可使用 API 当即看到查询结果。后端
len(). 调用 QuerySet 的 len() 方法,查询就会被运行。这正如你所料,会返回查询结果列表的长度。数组
注意:若是你想获得集合中记录的数量,就不要使用 QuerySet 的 len() 方法。由于直接在数据库层面使用 SQL 的 SELECT COUNT(*) 会更加高效,Django 提供了 count() 方法就是这个缘由。详情参阅下面的 count() 方法。缓存
list(). 对 QuerySet 应用 list() 方法,就会运行查询。例如:安全
entry_list = list(Entry.objects.all())
要注意地是:使用这个方法会占用大量内存,由于 Django 将列表内容都载入到内存中。作为对比,遍历 QuerySet 是从数据库读取数据,仅在使用某个对象时才将其载入到内容中。ide
Pickling QuerySets
若是你要 序列化(pickle) 一个 QuerySet,Django 首先就会将查询对象载入到内存中以完成序列化,这样你就能够第一时间使用对象(直接从数据库中读取数据须要必定时间,这正是缓存所想避免的)。而序列化是缓存化的先行工做,因此在缓存查询时,首先就会进行序列化工做。这意味着当你反序列化 QuerySet 时,第一时间就会从内存中得到查询的结果,而不是从数据库中查找。
若是你只是想序列化部分必要的信息以便晚些时候能够从数据库中重建 Queryset ,那只序列化 QuerySet 的 query 属性便可。接下来你就可使用下面的代码重建原来的 QuerySet (这当中没有数据库读取):
>>> import pickle >>> query = pickle.loads(s) # Assuming 's' is the pickled string. >>> qs = MyModel.objects.all() >>> qs.query = query # Restore the original 'query'.
query 属性是一个不透明的对象。这就意味着它的内部结构并非公开的。即使如此,对于本节提到的序列化和反序列化来讲,它还是安全和被彻底支持的。
filter(**kwargs)
返回一个新的 QuerySet ,它包含了与所给的筛选条件相匹配的对象。
这些筛选条件(**kwargs)在下面的字段筛选(Field lookups) 中有详细介绍。多个条件之间在 SQL 语句中是 AND 关系。
exclude(**kwargs)
返回一个新的 QuerySet,它包含那些与所给筛选条件不匹配的对象。
这些筛选条件(**kwargs)也在下面的 字段筛选(Field lookups) 中有详细描述。多个条件之间在 SQL 语句中也是 AND 关系,可是总体又是一个 NOT() 关系。
下面的例子剔除了出版日期 pub_date 晚于 2005-1-3 而且大标题 headline 是 "Hello" 的全部博文(entry):
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
在 SQL 语句中,这等价于:
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
下面的例子剔除出版日期 pub_date 晚于 2005-1-3 或者大标题是 "Hello" 的全部博文:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
在 SQL 语句中,这等价于:
SELECT ... WHERE NOT pub_date > '2005-1-3' OR NOT headline = 'Hello'
要注意第二个例子是有不少限制的。
annotate(*args, **kwargs)
咱们能够为 QuerySet 中的每一个对象添加注解。能够经过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也能够是平均值或总和,等等),作为 QuerySet 中对象的注解。annotate() 中的每一个参数都会被作为注解添加到 QuerySet 中返回的对象。
Django 提供的注解函式在下面的 (注解函式Aggregation Functions) 有详细介绍。
注解会使用关键字参数来作为注解的别名。其中任何参数都会生成一个别名,它与注解函式的名称和被注解的 model 相关。
例如,你正在操做一个博客列表,你想知道一个博客究竟有多少篇博文:
>>> from django.db.models import Count >>> q = Blog.objects.annotate(Count('entry')) # The name of the first blog >>> q[0].name 'Blogasaurus' # The number of entries on the first blog >>> q[0].entry__count 42
Blog model 类自己并无定义 entry__count 属性,但可使用注解函式的关系字参数,从而改变注解的命名:
>>> q = Blog.objects.annotate(number_of_entries=Count('entry')) # The number of entries on the first blog, using the name provided >>> q[0].number_of_entries 42
order_by(*fields)
默认状况下, QuerySet 返回的查询结果是根据 model 类的 Meta 设置所提供的 ordering 项中定义的排序元组来进行对象排序的。你可使用 order_by 方法覆盖以前 QuerySet 中的排序设置。
例如:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
返回结果就会先按照 pub_date 进行升序排序,再按照 headline 进行降序排序。 "-pub_date" 前面的负号"-"表示降序排序。默认是采用升序排序。要随机排序,就使用 "?",例如:
Entry.objects.order_by('?')
注意:order_by('?') 可能会很是缓慢和消耗过多资源,这取决于你所使用的数据库。
要根据其余 model 字段排序,所用语法和跨关系查询的语法相同。就是说,用两个连续的下划线(__)链接关联 model 和 要排序的字段名称, 并且能够一直延伸。例如:
Entry.objects.order_by('blog__name', 'headline')
若是你想对关联字段排序,在没有指定 Meta.ordering 的状况下,Django 会采用默认排序设置,就是按照关联 model 的主键进行排序。例如:
Entry.objects.order_by('blog')
等价于:
Entry.objects.order_by('blog__id')
这是由于 Blog model 没有声明排序项的原故。
Django 1.7新添加:
# No Join Entry.objects.order_by('blog_id') #能够避免JOIN的代价 # Join Entry.objects.order_by('blog__id')
若是你使用了 distinct() 方法,那么在对关联字段排序时要格外谨慎。
在 Django 当中是能够按照多值字段(例如 ManyToMany 字段)进行排序的。不过,这个特性虽然先进,可是并不实用。除非是你已经很清楚过滤结果或可用数据中的每一个对象,都只有一个相关联的对象时(就是至关于只是一对一关系时),排序才会符合你预期的结果,因此对多值字段排序时要格外注意。
若是你不想对任何字段排序,也不想使用 model 中原有的排序设置,那么能够调用无参数的 order_by() 方法。
对于排序项是否应该大小写敏感,Django 并无提供设置方法,这彻底取决于后端的数据库对排序大小写如何处理。
你能够令某个查询结果是可排序的,也能够是不可排序的,这取决于 QuerySet.ordered 属性。若是它的值是 True ,那么 QuerySet 就是可排序的。
reverse()
使用 reverse() 方法会对查询结果进行反向排序。调用两次 reverse() 方法至关于排序没发生改过。
要获得查询结果中最后五个对象,能够这样写:
my_queryset.reverse()[:5]
要注意这种方式与 Python 语法中的从尾部切片是彻底不同的。在上面的例子中,是先获得最后一个元素,而后是倒数第二个,依次处理。可是若是咱们有一个 Python 队列,使用 seq[-5:]时,倒是先获得倒数第五个元素。Django 之因此采用 reverse 来获取倒数的记录,而不支持切片的方法,缘由就是后者在 SQL 中难以作好。
还有一点要注意,就是 reverse() 方法应该只做用于已定义了排序项 QuerySet (例如,在查询时使用了order_by()方法,或是在 model 类当中直接定义了排序项). 若是并无明肯定义排序项,那么调用 QuerySet, calling reverse() 就没什么实际意义(由于在调用 reverse() 以前,数据没有定义排序,因此在这以后也不会进行排序。)
distinct()
返回一个新的 QuerySet ,它会在执行 SQL 查询时使用 SELECT DISTINCT。这意味着返回结果中的重复记录将被剔除。
默认状况下, QuerySet 并会剔除重复的记录。在实际当中,这不是什么问题,由于象 Blog.objects.all() 这样的查询并不会产生重复的记录。可是,若是你使用 QuerySet 作多表查询时,就极可能会产生重复记录。这时,就可使用 distinct() 方法。
Note
在 order_by(*fields) 中出现的字段也会包含在 SQL SELECT 列中。若是和 distinct() 同时使用,有时返回的结果却与预想的不一样。这是由于:若是你对跨关系的关联字段进行排序,这些字段就会被添加到被选取的列中,这就可能产生重复数据(好比,其余的列数据都相同,只是关联字段的值不一样)。但因为 order_by 中的关联字段并不会出如今返回结果中(他们仅仅是用来实现order),因此有时返回的数据看上去就象是并无进行过 distinct 处理同样。
一样的缘由,若是你用 values() 方法得到被选取的列,就会发现包含在 order_by() (或是 model 类的 Meta 中设置的排序项)中的字段也包含在里面,就会对返回的结果产生影响。
这章节强调的就是在你使用 distinct() 时,要谨慎对待关联字段排序。一样的,在同时使用 distinct() 和 values() 时,若是排序字段并无出如今 values() 返回的结果中,那么也要引发注意。
values(*fields)
返回一个 ValuesQuerySet ----一个特殊的 QuerySet ,运行后获得的并非一系列 model 的实例化对象,而是一个可迭代的字典序列。
每一个字典都表示一个对象,而键名就是 model 对象的属性名称。
下面的例子就对 values() 获得的字典与传统的 model 对象进行了对比:
# This list contains a Blog object. >>> Blog.objects.filter(name__startswith='Beatles') [<Blog: Beatles Blog>] # This list contains a dictionary. >>> Blog.objects.filter(name__startswith='Beatles').values() [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]
values() 能够接收可选的位置参数,*fields,就是字段的名称,用来限制 SELECT 选取的数据。若是你指定了字段参数,每一个字典就会以 Key-Value 的形式保存你所指定的字段信息;若是没有指定,每一个字典就会包含当前数据表当中的全部字段信息。
例如:
>>> Blog.objects.values() [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}], >>> Blog.objects.values('id', 'name') [{'id': 1, 'name': 'Beatles Blog'}]
下面这些细节值得注意:
若是你有一个名为 foo 的ForeignKey 字段,默认状况下调用 values() 返回的字典中包含键名为 foo_id 的字典项,由于它是一个隐含的 model 字段,用来保存关联对象的主键值( foo 属性用来联系相关联的 model )。当你使用 values() 并传递字段名称时, 传递foo 或 foo_id 都会获得相同的结果 (字典中的键名会自动换成你传递的字段名称)。
例如:
>>> Entry.objects.values() [{'blog_id': 1, 'headline': u'First Entry', ...}, ...] >>> Entry.objects.values('blog') [{'blog': 1}, ...] >>> Entry.objects.values('blog_id') [{'blog_id': 1}, ...]
在 values() 和 distinct() 同时使用时,要注意排序项会影响返回的结果,详情请查看上面 distinct() 一节。
在values()以后使用defer()和only()是无用的。
ValuesQuerySet 是很是有用的。利用它,你就能够只得到你所需的那部分数据,而没必要同时读取其余的无用数据。
最后,要提醒的是,ValuesQuerySet 是 QuerySet 的一个子类,因此它拥有 QuerySet 全部的方法。你能够对它调用 filter() 或是 order_by() 以及其余方法。因此下面俩种写法是等价的:
Blog.objects.values().order_by('id') Blog.objects.order_by('id').values()
Django 的编写者们更喜欢第二种写法,就是先写影响 SQL 的方法,再写影响输出的方法(好比例中先写 order,再写values ),但这些都可有可无,彻底视你我的喜爱而定。
也能够指向一对1、多对多、外键关系对象的域:
Blog.objects.values('name', 'entry__headline') [{'name': 'My blog', 'entry__headline': 'An entry'}, {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]
当指向多对多关系时,由于关联对象可能有不少,因此同一个对象会根据不一样的多对多关系返回屡次。
values_list(*fields)
它与 values() 很是类似,只不事后者返回的结果是字典序列,而 values() 返回的结果是元组序列。每一个元组都包含传递给 values_list() 的字段名称和内容。好比第一项就对应着第一个字段,例如:
>>> Entry.objects.values_list('id', 'headline') [(1, u'First entry'), ...]
若是你传递了一个字段作为参数,那么你可使用 flat 参数。若是它的值是 True,就意味着返回结果都是单独的值,而不是元组。下面的例子会讲得更清楚:
>>> Entry.objects.values_list('id').order_by('id') [(1,), (2,), (3,), ...] >>> Entry.objects.values_list('id', flat=True).order_by('id') [1, 2, 3, ...]
若是传递的字段不止一个,使用 flat 就会致使错误。
若是你没给 values_list() 传递参数,它就会按照字段在 model 类中定义的顺序返回全部的字段。
注意这个方法返回的是 ValuesListQuerySet对象,和列表类似但并非列表,须要列表操做时需list()转为列表。
dates(field, kind, order='ASC')
返回一个 DateQuerySet ,就是提取 QuerySet 查询中所包含的日期,将其组成一个新的 datetime.date 对象的列表。
field 是你的 model 中的 DateField 字段名称。
kind 是 "year", "month" 或 "day" 之一。 每一个 datetime.date对象都会根据所给的 type 进行截减。
"year" 返回全部时间值中非重复的年分列表。
"month" 返回全部时间值中非重复的年/月列表。
"day" 返回全部时间值中非重复的年/月/日列表。
order, 默认是 'ASC',只有两个取值 'ASC' 或 'DESC'。它决定结果如何排序。
例子:
>>> Entry.objects.dates('pub_date', 'year') [datetime.date(2005, 1, 1)] >>> Entry.objects.dates('pub_date', 'month') [datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)] >>> Entry.objects.dates('pub_date', 'day') [datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)] >>> Entry.objects.dates('pub_date', 'day', order='DESC') [datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)] >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') [datetime.date(2005, 3, 20)]
datetimes(field, kind, order='ASC')
返回一个 DateTimeQuerySet ,就是提取 QuerySet 查询中所包含的日期,将其组成一个新的 datetime.datetime 对象的列表。
none()
返回一个 EmptyQuerySet -- 它是一个运行时只返回空列表的 QuerySet。它常常用在这种场合:你要返回一个空列表,可是调用者却须要接收一个 QuerySet 对象。(这时,就能够用它代替空列表)
例如:
>>> Entry.objects.none() [] >>> from django.db.models.query import EmptyQuerySet >>> isinstance(Entry.objects.none(), EmptyQuerySet) True
all()
返回当前 QuerySet (或者是传递的 QuerySet 子类)的一分拷贝。 这在某些场合是很用的,好比,你想对一个 model manager 或是一个 QuerySet 的查询结果作进一步的过滤。你就能够调用 all() 得到一分拷贝以继续操做,从而保证原 QuerySet 的安全。
当一个QuerySet查询后,它会缓存查询结果。若是数据库发生了改变,就能够调用all()来更新查询过的QuerySet。
select_related()
返回一个 QuerySet ,它会在执行查询时自动跟踪外键关系,从而选取所关联的对象数据。它是一个增效器,虽然会致使较大的数据查询(有时会很是大),可是接下来再使用外键关系得到关联对象时,就会再也不次读取数据库了。
下面的例子展现在得到关联对象时,使用 select_related() 和不使用的区别,首先是不使用的例子:
# Hits the database. e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. b = e.blog
接下来是使用 select_related 的例子:
# Hits the database. e = Entry.objects.select_related().get(id=5) # Doesn't hit the database, because e.blog has been prepopulated # in the previous query. b = e.blog
select_related() 会尽量地深刻遍历外键链接。例如:
from django.db import models class City(models.Model): # ... pass class Person(models.Model): # ... hometown = models.ForeignKey(City) class Book(models.Model): # ... author = models.ForeignKey(Person)
接下来调用 Book.objects.select_related().get(id=4) 将缓存关联的 Person 和 City:
b = Book.objects.select_related('person__city').get(id=4) p = b.author # Doesn't hit the database. c = p.hometown # Doesn't hit the database. b = Book.objects.get(id=4) # No select_related() in this example. p = b.author # Hits the database. c = p.hometown # Hits the database.
prefetch_related()
对于多对多字段(ManyToManyField)和一对多字段,可使用prefetch_related()来进行优化。或许你会说,没有一个叫OneToManyField的东西啊。实际上 ,ForeignKey就是一个多对一的字段,而被ForeignKey关联的字段就是一对多字段了。
prefetch_related()和select_related()的设计目的很类似,都是为了减小SQL查询的数量,可是实现的方式不同。后者是经过JOIN语句,在SQL查询内解决问题。可是对于多对多关系,使用SQL语句解决就显得有些不太明智,由于JOIN获得的表将会很长,会致使SQL语句运行时间的增长和内存占用的增长。如有n个对象,每一个对象的多对多字段对应Mi条,就会生成Σ(n)Mi 行的结果表。
prefetch_related()的解决方法是,分别查询每一个表,而后用Python处理他们之间的关系。
例如:
from django.db import models class Topping(models.Model): name = models.CharField(max_length=30) class Pizza(models.Model): name = models.CharField(max_length=50) toppings = models.ManyToManyField(Topping) def __str__(self): # __unicode__ on Python 2 return "%s (%s)" % (self.name, ", ".join([topping.name for topping in self.toppings.all()]))
运行
>>> Pizza.objects.all().prefetch_related('toppings')
能够联合查询:
class Restaurant(models.Model): pizzas = models.ManyToMany(Pizza, related_name='restaurants') best_pizza = models.ForeignKey(Pizza, related_name='championed_by')
下面的例子均可以
>>> Restaurant.objects.prefetch_related('pizzas__toppings') >>> Restaurant.objects.prefetch_related('best_pizza__toppings') >>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
有些状况下,Django 的查询语法难以简练地表达复杂的 WHERE 子句。对于这种状况,Django 提供了 extra() QuerySet 修改机制,它能在QuerySet 生成的 SQL 从句中注入新子句。
因为产品差别的缘由,这些自定义的查询难以保障在不一样的数据库之间兼容(由于你手写 SQL 代码的缘由),并且违背了 DRY 原则,因此如非必要,仍是尽可能避免写 extra。
在 extra 能够指定一个或多个 params 参数,如 select,where 或 tables。全部参数都是可选的,但你至少要使用一个。
select
select 参数可让你在 SELECT 从句中添加其余字段信息。它应该是一个字典,存放着属性名到 SQL 从句的映射。
例如:
Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
结果中每一个 Entry 对象都有一个额外的 is_recent 属性,它是一个布尔值,表示 pub_date 是否晚于2006年1月1号。
Django 会直接在 SELECT 中加入对应的 SQL 片段,因此转换后的 SQL 以下:
SELECT blog_entry.*, (pub_date > '2006-01-01') FROM blog_entry;
下面这个例子更复杂一些;它会在每一个 Blog 对象中添加一个 entry_count 属性,它会运行一个子查询,获得相关联的 Entry 对象的数量:
Blog.objects.extra( select={ 'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id' }, )
(在上面这个特例中,咱们要了解这个事实,就是 blog_blog 表已经存在于 FROM 从句中。)
翻译成 SQL 以下:
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count FROM blog_blog;
要注意的是,大多数数据库须要在子句两端添加括号,而在 Django 的 select 从句中却无须这样。一样要引发注意的是,在某些数据库中,好比某些 MySQL 版本,是不支持子查询的。
某些时候,你可能想给 extra(select=...) 中的 SQL 语句传递参数,这时就可使用 select_params 参数。由于 select_params 是一个队列,而 select 属性是一个字典,因此二者在匹配时应正确地一一对应。在这种状况下中,你应该使用 django.utils.datastructures.SortedDict 匹配 select 的值,而不是使用通常的 Python 队列。
例如:
Blog.objects.extra( select=SortedDict([('a', '%s'), ('b', '%s')]), select_params=('one', 'two'))
在使用 extra() 时要避免在 select 字串含有 "%%s" 子串, 这是由于在 Django 中,处理 select 字串时查找的是 %s 而并不是转义后的 % 字符。因此若是对 % 进行了转义,反而得不到正确的结果。
where / tables
你可使用 where 参数显示定义 SQL 中的 WHERE 从句,有时也能够运行非显式地链接。你还可使用 tables 手动地给 SQL FROM 从句添加其余表。
where 和 tables 都接受字符串列表作为参数。全部的 where 参数彼此之间都是 "AND" 关系。
例如:
Entry.objects.extra(where=['id IN (3, 4, 5, 20)'])
大体能够翻译为以下的 SQL:
SELECT * FROM blog_entry WHERE id IN (3, 4, 5, 20);
在使用 tables 时,若是你指定的表在查询中已出现过,那么要格外当心。当你经过 tables 参数添加其余数据表时,若是这个表已经被包含在查询中,那么 Django 就会认为你想再一次包含这个表。这就致使了一个问题:因为重复出现屡次的表会被赋予一个别名,因此除了第一次以外,每一个重复的表名都会分别由 Django 分配一个别名。因此,若是你同时使用了 where 参数,在其中用到了某个重复表,殊不知它的别名,那么就会致使错误。
通常状况下,你只会添加一个未在查询中出现的新表。可是若是上面所提到的特殊状况发生了,那么能够采用以下措施解决。首先,判断是否有必要要出现重复的表,可否将重复的表去掉。若是这点行不通,就试着把 extra() 调用放在查询结构的起始处,由于首次出现的表名不会被重命名,因此可能能解决问题。若是这也不行,那就查看生成的 SQL 语句,从中找出各个数据库的别名,而后依此重写 where 参数,由于只要你每次都用一样的方式调用查询(queryset),表的别名都不会发生变化。因此你能够直接使用表的别名来构造 where。
order_by
若是你已经过 extra() 添加了新字段或是数据库,此时若想对新字段进行排序,就能够给 extra() 中的 order_by 参数传递一个排序字符串序列。字符串能够是 model 原生的字段名(与使用普通的 order_by() 方法同样),也能够是 table_name.column_name 这种形式,或者是你在 extra() 的 select 中所定义的字段。
例如:
q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) q = q.extra(order_by = ['-is_recent'])
这段代码按照 is_recent 对记录进行排序,字段值是 True 的排在前面,False 的排在后面。(True 在降序排序时是排在 False 的前面)。
顺便说一下,上面这段代码同时也展现出,能够依你所愿的那样屡次调用 extra() 操做(每次添加新的语句结构便可)。
params
上面提到的 where 参数还能够用标准的 Python 占位符 -- '%s' ,它能够根据数据库引擎自动决定是否添加引号。 params 参数是用来替换占位符的字符串列表。
例如:
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
使用 params 替换 where 的中嵌入值是一个很是好的作法,这是由于 params 能够根据你的数据库判断要不要给传入值添加引号(例如,传入值中的引号会被自动转义)。
很差的用法:
Entry.objects.extra(where=["headline='Lennon'"])
优雅的用法:
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
defer(*fields)
在某些数据复杂的环境下,你的 model 可能包含很是多的字段,可能某些字段包含很是多的数据(好比,文档字段),或者将其转化为 Python 对象会消耗很是多的资源。在这种状况下,有时你可能并不须要这种字段的信息,那么你可让 Django 不读取它们的数据。
将不想载入的字段的名称传给 defer() 方法,就能够作到延后载入:
Entry.objects.defer("lede", "body")
延后截入字段的查询返回的还是 model 类的实例。在你访问延后载入字段时,你仍能够得到字段的内容,所不一样的是,内容是在你访问延后字段时才读取数据库的,而普通字段是在运行查询(queryset)时就一次性从数据库中读取数据的。
你能够屡次调用 defer() 方法。每一个调用均可以添加新的延后载入字段:
# Defers both the body and lede fields. Entry.objects.defer("body").filter(headline="Lennon").defer("lede")
对延后载入字段进行排序是不会起做用的;重复添加延后载入字段也不会有何不良影响。
你也能够延后载入关联 model 中的字段(前提是你使用 select_related() 载入了关联 model),用法也是用双下划线链接关联字段:
Blog.objects.select_related().defer("entry__lede", "entry__body")
若是你想清除延后载入的设置,只要使用将 None 作为参数传给 defer() 便可:
# Load all fields immediately. my_queryset.defer(None)
有些字段不管你如何指定,都不会被延后加载。好比,你永远不能延后加载主键字段。若是你使用 select_related() 得到关联 model 字段信息,那么你就不能延后载入关联 model 的主键。(若是这样作了,虽然不会抛出错误,事实上却不完成延后加载)
Note
defer() 方法(和随后提到的 only() 方法) 都只适用于特定状况下的高级属性。它们能够提供性能上的优化,不过前提是你已经对你用到的查询有过很深刻细致的分析,很是清楚你须要的到底是哪些信息,并且已经对你所须要的数据和默认状况下返回的全部数据进行比对,清楚二者之间的差别。这完成了上述工做以后,再使用这两种方法进行优化才是有意义的。因此当你刚开始构建你的应用时,先不要急着使用 defer() 方法,等你已经写完查询而且分析成哪些方面是热点应用之后,再用也不迟。
only(*fields)
only() 方法或多或少与 defer() 的做用相反。若是你在提取数据时但愿某个字段不该该被延后载入,而应该当即载入,那么你就能够作使用 only() 方法。若是你一个 model ,你但愿它全部的字段都延后加载,只有某几个字段是当即载入的,那么你就应该使用 only() 方法。
若是你有一个 model,它有 name, age 和 biography 三个字段,那么下面两种写法效果同样的:
Person.objects.defer("age", "biography") Person.objects.only("name")
你不管什么时候调用 only(),它都会马上更改载入设置。这与它的命名很是相符:只有 only 中的字段会当即载入,而其余的则都是延后载入的。所以,连续调用 only() 时,只有最后一个 only 方法才会生效:
# This will defer all fields except the headline. Entry.objects.only("body", "lede").only("headline")
因为 defer() 能够递增(每次都添加字段到延后载入的列表中),因此你能够将 only() 和 defer() 结合在一块儿使用,请注意使用顺序,先 only 然后 defer:
# Final result is that everything except "headline" is deferred. Entry.objects.only("headline", "body").defer("body") # Final result loads headline and body immediately (only() replaces any # existing set of fields). Entry.objects.defer("body").only("headline", "body")
using(alias)
使用多个数据库时使用,参数是数据库的alias
# queries the database with the 'default' alias. >>> Entry.objects.all() # queries the database with the 'backup' alias >>> Entry.objects.using('backup')
select_for_update(nowait=False)
返回queryset,并将须要更新的行锁定,相似于SELECT ... FOR UPDATE的操做。
entries = Entry.objects.select_for_update().filter(author=request.user)
全部匹配的entries都会被锁定直到这次事务结束。
raw(raw_query, params=None, translations=None)
执行raw SQL queries
下面所列的 QuerySet 方法做用于 QuerySet,却并不返回 other than a QuerySet。
这些方法并不使用缓存(请查看 缓存与查询(Caching and QuerySets))。因此它们在运行时是当即读取数据库的。
get(**kwargs)
返回与所给的筛选条件相匹配的对象,筛选条件在 字段筛选条件(Field lookups) 一节中有详细介绍。
在使用 get() 时,若是符合筛选条件的对象超过一个,就会抛出 MultipleObjectsReturned 异常。MultipleObjectsReturned 是 model 类的一个属性。
在使用 get() 时,若是没有找到符合筛选条件的对象,就会抛出 DoesNotExist 异常。这个异常也是 model 对象的一个属性。例如:
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.")
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 参数在别处有详细介绍,它表示把当前 model 当成一个新对象来建立。通常状况下,你没必要担忧这一点,可是若是你的 model 的主键是你手动指定的,并且它的值已经在数据库中存在,那么调用 create() 就会失败,并抛出 IntegrityError。这是由于主键值必须是惟一的。因此当你手动指定主键时,记得要作好处理异常的准备。
get_or_create(defaults=None,**kwargs)
这是一个方便实际应用的方法,它根据所给的筛选条件查询对象,若是对象不存在就建立一个新对象。
它返回的是一个 (object, created) 元组,其中的 object 是所读取或是建立的对象,而 created 则是一个布尔值,它表示前面提到的 object 是不是新建立的。
这意味着它能够有效地减小代码,而且对编写数据导入脚本很是有用。例如:
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()
上面的代码会随着 model 中字段数量的激增而变得愈发庸肿。接下来用 get_or_create() 重写:
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon', defaults={'birthday': date(1940, 10, 9)})
在这里要注意 defaults 是一个字典,它仅适用于建立对象时为字段赋值,而并不适用于查找已存在的对象。 get_or_create() 所接收的关键字参数都会在调用 get() 时被使用,有一个参数例外,就是 defaults。在使用get_or_create() 时若是找到了对象,就会返回这个对象和 False。若是没有找到,就会实例化一个新对象,并将其保存;同时返回这个新对象和 True。建立新对象的步骤大体以下:
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) params.update(defaults) obj = self.model(**params) obj.save()
用天然语言描述:从非 'defaults' 关键字参数中排除含有双下划线的参数(由于双下划线表示非精确查询),而后再添加 defaults 字典的内容,若是键名与已有的关键字参数重复,就以 defaults 中的内容为准, 而后将整理后的关键字参数传递给 model 类。固然,这只是算法的简化描述,实际上对不少细节没有说起,好比对异常和边界条件的处理。若是你对此感兴趣,不妨看一下原代码。
若是你的 model 恰巧有一个字段,名称正是 defaults,并且你想在 get_or_create() 中用它作为精确查询的条件, 就得使用 'defaults__exact' (以前提过 defaults 只能在建立时对对象赋值,而不能进行查询),象下面这样:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
若是你手动指定了主键,那么使用 get_or_create() 方法时也会象 create() 同样,抛出相似的异常。当你手动指定了主键,若主键值已经在数据库中存在,就会抛出一个 IntegrityError 异常。
最后提一下在 Django 视图(views)中使用 get_or_create() 时要注意的一点。如上所说,对于在脚本中分析数据和添加新数据而言,get_or_create() 是很是有用的。可是若是你是在视图中使用 get_or_create() ,那么就要格外留意,要确认是在 POST 请求中使用,除非你有很必要和很充分的理由才不这么作。而在 GET 请求中使用的话,不会对数据产生任何做用。而使用 POST 的话,每一个发往页面的请求都会对数据有必定的反作用。
Note
经过多对多关系使用时要注意:
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)
运行:
>>> 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
不和book相关联的Chapter就不会被查找到了。
update_or_create(defaults=None, **kwargs)
与上面相似
try: obj = Person.objects.get(first_name='John', last_name='Lennon') for key, value in updated_values.iteritems(): setattr(obj, key, value) obj.save() except Person.DoesNotExist: updated_values.update({'first_name': 'John', 'last_name': 'Lennon'}) obj = Person(**updated_values) obj.save()
能够简写为:
obj, created = Person.objects.update_or_create( first_name='John', last_name='Lennon', defaults=updated_values)
bulk_create(objs, batch_size=None)
批量建立
>>> Entry.objects.bulk_create([ ... Entry(headline="Django 1.0 Released"), ... Entry(headline="Django 1.1 Announced"), ... Entry(headline="Breaking: Django is awesome") ... ])
优于:
Entry.objects.create(headline="Python 1.0 Released") Entry.objects.create(headline="Python 1.1 Planned")
count()
返回数据库中匹配查询(QuerySet)的对象数量。 count() 不会抛出任何异常。
例如:
# 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()
count() 会在后端执行 SELECT COUNT(*) 操做,因此你应该尽可能使用 count() 而不是对返回的查询结果使用 len() 。
根据你所使用的数据库(例如 PostgreSQL 和 MySQL),count() 可能会返回长整型,而不是普通的 Python 整数。这确实是一个很古怪的举措,没有什么实际意义。
in_bulk(id_list)
接收一个主键值列表,而后根据每一个主键值所其对应的对象,返回一个主键值与对象的映射字典。
>>> 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([]) {}
若是你给 in_bulk() 传递的是一个空列代表,获得就是一个空字典。
iterator()
运行查询(QuerySet),而后根据结果返回一个 迭代器(iterator。 作为比较,使用 QuerySet 时,从数据库中读取全部记录后,一次性将全部记录实例化为对应的对象;而 iterator() 则是读取记录后,是分屡次对数据实例化,用到哪一个对象才实例化哪一个对象。相对于一次性返回不少对象的 QuerySet,使用迭代器不只效率更高,并且更节省内存。
要注意的是,若是将 iterator() 做用于 QuerySet,那就意味着会再一次运行查询,就是说会运行两次查询。
latest(field_name=None)
根据时间字段 field_name 获得最新的对象。
下面这个例子根据 pub_date 字段获得数据表中最新的 Entry 对象:
Entry.objects.latest('pub_date')
若是你在 model 中 Meta 定义了 get_latest_by 项, 那么你能够略去 field_name 参数。Django 会将 get_latest_by 作为默认设置。
和 get(), latest() 同样,若是根据所给条件没有找到匹配的对象,就会抛出 DoesNotExist 异常。
注意 latest()和earliest() 是纯粹为了易用易读而存在的方法。
earliest(field_name=None)
相似于latest()
first()
p = Article.objects.order_by('title', 'pub_date').first()
至关于:
try: p = Article.objects.order_by('title', 'pub_date')[0] except IndexError: p = None
last()
相似于first()
aggregate(*args, **kwargs)
经过对 QuerySet 进行计算,返回一个聚合值的字典。 aggregate() 中每一个参数都指定一个包含在字典中的返回值。
聚合使用关键字参数作为注解的名称。每一个参数都有一个为其订作的名称,这个名称取决于聚合函式的函数名和聚合字段的名称。
例如,你正在处理博文,你想知道博客中一共有多少篇博文:
>>> from django.db.models import Count >>> q = Blog.objects.aggregate(Count('entry')) {'entry__count': 16}
经过在 aggregate 指定关键字参数,你能够控制返回的聚合名称:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) {'number_of_entries': 16}
exists()
若是 QuerySet 包含有数据,就返回 True 不然就返回 False。这多是最快最简单的查询方法了.
update()
更新
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
delete()
删除
>>> b = Blog.objects.get(pk=1) # Delete all the entries belonging to this Blog. >>> Entry.objects.filter(blog=b).delete()
字段筛选条件决定了你如何构造 SQL 语句中的 WHERE 从句。它们被指定为 QuerySet 中 filter(),exclude() 和 get() 方法的关键字参数。
exact
精确匹配。若是指定的值是 None,就会翻译成 SQL 中的 NULL (详情请查看 isnull )。
例如:
Entry.objects.get(id__exact=14) Entry.objects.get(id__exact=None)
等价的 SQL:
SELECT ... WHERE id = 14; SELECT ... WHERE id IS NULL;
iexact
忽略大小写的匹配。
例如:
Blog.objects.get(name__iexact='beatles blog') Blog.objects.get(name__iexact=None)
等价于以下 SQL :
SELECT ... WHERE name ILIKE 'beatles blog'; SELECT ... WHERE name IS NULL;
SQLite 用户要注意
在使用 SQLite 做为数据库,而且应用 Unicode (non-ASCII) 字符串时,请先查看 database note 中关于字符串比对那一节内容。SQLite 对 Unicode 字符串,没法作忽略大小写的匹配。
contains
大小写敏感的包含匹配。
例如:
Entry.objects.get(headline__contains='Lennon')
等价于 SQL :
SELECT ... WHERE headline LIKE '%Lennon%';
要注意,上述语句将匹配大标题 'Today Lennon honored' ,但不能匹配 'today lennon honored'。
SQLite 不支持大小写敏感的 LIKE 语句;因此对 SQLite 使用 contains 时就和使用 icontains 同样。
icontains
忽略大小写的包含匹配。
例如:
Entry.objects.get(headline__icontains='Lennon')
等价于 SQL:
SELECT ... WHERE headline ILIKE '%Lennon%';
SQLite 用户请注意
使用 SQLite 数据库并应用 Unicode (non-ASCII) 字符串时,请先查看 database note 文档中关于字符串比对那一节内容。
in
是否在一个给定的列表中。
例如:
Entry.objects.filter(id__in=[1, 3, 4])
等价于 SQL:
SELECT ... WHERE id IN (1, 3, 4);
你也能够把查询(queryset)结果当作动态的列表,从而代替固定的列表:
inner_qs = Blog.objects.filter(name__contains='Cheddar') entries = Entry.objects.filter(blog__in=inner_qs)
作动态列表的 queryset 运行时就会被作为一个子查询:
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
若是你传递了一个 ValuesQuerySet 或 ValuesListQuerySet (它们是调用查询集上 values() 和 values_list() 方法的返回结果) 作为 __in 条件的值,那么你要确认只匹配返回结果中的一个字段。例如,下面的代码能正常的工做(对博客名称进行过滤):
inner_qs = Blog.objects.filter(name__contains='Ch').values('name') entries = Entry.objects.filter(blog__name__in=inner_qs)
下面的代码却会抛出异常,缘由是内部的查询会尝试匹配两个字段值,但只有一个是有用的:
# Bad code! Will raise a TypeError. inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id') entries = Entry.objects.filter(blog__name__in=inner_qs)
warning
query 属性本是一个不公开的内部属性,虽然他在上面的代码中工做得很好,可是它的API极可能会在不一样的 Django 版本中常常变更。
性能考虑
要谨慎使用嵌套查询,而且要对你所采用的数据库性能有所了解(若是不了解,就去作一下性能测试)。有些数据库,好比著名的MySQL,就不能很好地优化嵌套查询。因此在上面的案例中,先在第一个查询中提取值列表,而后再将其传递给第二个查询,会对性能有较高的提高。说白了,就是用两个高效的查询替换掉一个低效的查询:
values = Blog.objects.filter( name__contains='Cheddar').values_list('pk', flat=True) entries = Entry.objects.filter(blog__in=list(values))
gt
大于。
gte
大于等于。
lt
小于。
lte
小于等于。
startswith
大小写敏感的以....开头。
istartswith
忽略大小写的以....开头。
endswith
大小写敏感的以....结尾。
iendswith
忽略大小写的以....结尾。
range
包含的范围。
例如:
import datetime start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
等价于 SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
你能够把 range 当成 SQL 中的 BETWEEN 来用,好比日期,数字,甚至是字符。
year
对日期/时间字段精确匹配年分,年分用四位数字表示。
例如:
Entry.objects.filter(pub_date__year=2005)
等价于 SQL:
SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005';
(不一样的数据库引擎中,翻译获得的 SQL 也不尽相同。)
month
对日期/时间字段精确匹配月分,用整数表示月分,好比 1 表示一月,12 表示十二月。
day
对日期/时间字段精确匹配日期。
要注意的是,这个匹配只会获得全部 pub_date 字段内容是表示 某月的第三天 的记录,如一月三号,六月三号。而十月二十三号就不在此列。
week_day
对日期/时间字段匹配星期几
例如:
Entry.objects.filter(pub_date__week_day=2)
等价于 SQL:
SELECT ... WHERE EXTRACT('dow' FROM pub_date) = '2';
(不一样的数据库引擎中,翻译获得的 SQL 也不尽相同。)
要注意的是,这段代码将获得 pub_date 字段是星期一的全部记录 (西方习惯于将星期一看作一周的次日),与它的年月信息无关。星期以星期天作为第一天,以星期六作为最后一天。
hour
minute
second
isnull
根据 SQL 查询是空 IS NULL 仍是非空 IS NOT NULL,返回相应的 True 或 False。
例如:
Entry.objects.filter(pub_date__isnull=True)
等价于 SQL:
SELECT ... WHERE pub_date IS NULL;
search
利用全文索引作全文搜索。它与 contains 类似,但使用全文索引作搜索会更快一些。
例如:
Entry.objects.filter(headline__search="+Django -jazz Python")
等价于:
SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);
要注意这个方法仅适用于 MySQL ,而且要求设置全文索引。默认状况下 Django 使用 BOOLEAN MODE 模式。详见 Please check MySQL documentation for additional details.
regex
大小写敏感的正则表达式匹配。
它要求数据库支持正则表达式语法,而 SQLite 却没有内建正则表达式支持,所以 SQLite 的这个特性是由一个名为 REGEXP 的 Python 方法实现的,因此要用到 Python 的正则库 re.
例如:
Entry.objects.get(title__regex=r'^(An?|The) +')
等价于 SQL:
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'c'); -- Oracle SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
建议使用原生字符串 (例如,用 r'foo' 替换 'foo') 作为正则表达式。
iregex
忽略大小写的正则表达式匹配。
Avg
返回所给字段的平均值。
默认别名:<field>__avg
返回类型: float
Count
根据所给的关联字段返回被关联 model 的数量。
默认别名: <field>__count
返回类型: int
它有一个可选参数:
distinct
若是 distinct=True,那么只返回不重复的实例数量,至关于 SQL 中的 COUNT(DISTINCT field)。默认值是 False。
Max
默认别名: <field>__max
返回类型: 与所给字段值相同
Min
返回所给字段的最小值。
默认别名: <field>__min
返回类型: 与所给字段相同
StdDev
返回所给字段值的标准差。
默认别名: <field>__stddev
返回类型: float
它有一个可选参数:
sample
默认状况下, StdDev 返回一个整体误差值,可是若是 sample=True,则返回一个样本误差值。
SQLite
SQLite 自己并不提供 StdDev 支持,可使用 SQLite 的外置模块实现这个功能。详情请查看相应的 SQLite 文档,了解如何得到和安装扩展。
Sum
计算所给字段值的总和
默认别名: <field>__sum
返回类型: 与所给字段相同
Variance
返回所给字段值的标准方差。
默认别名: <field>__variance
返回类型: float
它有一个可选参数:
sample默认状况下, Variance 返回的是整体方差;若是 sample=True,返回的则是样式方差。