Django查询集 API

Stella981
• 阅读 547

查询集API 参考 

本文档描述查询集 API 的细节。它建立在模型和数据库查询指南的基础上,所以在阅读本文档之前,你也许需要首先阅读这两部分的文档。

本文档将通篇使用在数据库查询指南中用到的Weblog 模型的例子。

何时对查询集求值 

在内部,可以创建、过滤、切片和传递查询集而不用真实操作数据库。在你对查询集做求值之前,不会发生任何实际的数据库操作。

你可以使用下列方法对查询集求值:

迭代。查询集是可迭代的,它在首次迭代查询集时执行实际的数据库查询。例如, 下面的语句会将数据库中所有Entry 的headline 打印出来:

for e in Entry.objects.all():
    print(e.headline)

注意:不要使用上面的语句来验证在数据库中是否至少存在一条记录。使用 exists()方法更高效。

切片。 正如在限制查询集中解释的那样, 可以使用Python 的序列切片语法对一个查询集进行分片。一个未求值的查询集进行切片通常返回另一个未求值的查询集,但是如果你使用切片的”step“参数,Django 将执行数据库查询并返回一个列表。对一个已经求值的查询集进行切片将返回一个列表。

还要注意,虽然对未求值的查询集进行切片返回另一个未求值的查询集,但是却不可以进一步修改它了(例如,添加更多的Filter,或者修改排序的方式),因为这将不太好翻译成SQL而且含义也不清晰。

序列化/缓存。 序列化查询集的细节参见下面一节。本节提到它的目的是强调序列化将读取数据库。

repr()。 当对查询集调用repr() 时,将对它求值。这是为了在Python 交互式解释器中使用的方便,这样你可以在交互式使用这个API 时立即看到结果。

len()。 当你对查询集调用len() 时, 将对它求值。正如你期望的那样,返回一个查询结果集的长度。

注:如果你只需要知道集合中记录的个数(并不需要真实的对象),使用数据库层级的SELECT COUNT(*) 计数将更加高效。为此,Django 提供了 一个count() 方法.

list()。 对查询集调用list() 将强制对它求值。例如:

entry_list = list(Entry.objects.all())

bool()。 测试一个查询集的布尔值,例如使用bool()、or、and 或者if 语句将导致查询集的执行。如果至少有一个记录,则查询集为True,否则为False。例如:

if Entry.objects.filter(headline="Test"):
   print("There is at least one Entry with the headline Test")

注:如果你需要知道是否存在至少一条记录(而不需要真实的对象),使用 exists() 将更加高效。

Pickle 查询集

如果你Pickle一个查询集,它将在Pickle 之前强制将所有的结果加载到内存中。Pickle 通常用于缓存之前,并且当缓存的查询集重新加载时,你希望结果已经存在随时准备使用(从数据库读取耗费时间,就失去了缓存的目的)。这意味着当你Unpickle查询集时,它包含Pickle 时的结果,而不是当前数据库中的结果。

如果此后你只想Pickle 必要的信息来从数据库重新创建查询集,可以Pickle查询集的query 属性。然后你可以使用类似下面的代码重新创建原始的查询集(不用加载任何结果):

>>> import pickle
>>> query = pickle.loads(s)     # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query            # Restore the original 'query'.

query 是一个不透明的对象。它表示查询的内部构造,不属于公开的API。然而,这里讲到的Pickle 和Unpickle 这个属性的内容是安全的(和完全支持的)。

不可以在不同版本之间共享Pickle 的结果

查询集的Pickle 只能用于生成它们的Django 版本中。如果你使用Django 的版本N 生成一个Pickle,不保证这个Pickle 在Django 的版本N+1 中可以读取。Pickle 不可用于归档的长期策略。

New in Django 1.8.
因为Pickle 兼容性的错误很难诊断例如产生损坏的对象,当你试图Unpickle 的查询集与Pickle 时的Django 版本不同,将引发一个RuntimeWarning。

查询集 API 

下面是对于查询集的正式定义:

class QuerySet([model=None, query=None, using=None])[source] 

通常你在使用QuerySet时会以链式的filter 来使用。为了让这个能工作,大部分QuerySet 方法返回新的QuerySet。这些方法在本节将详细讲述。

QuerySet 类具有两个公有属性用于内省:

ordered[source] 
如果QuerySet 是排好序的则为True —— 例如有一个order_by() 子句或者模型有默认的排序。否则为False .

db[source] 
如果现在执行,则返回将使用的数据库。

QuerySet 存在query 参数是为了让具有特殊查询用途的子类如GeoQuerySet 可以重新构造内部的查询状态。这个参数的值是查询状态的不透明的表示,不是一个公开的API。简而言之:如果你有疑问,那么你实际上不需要使用它。

返回新的查询集 的方法

Django 提供了一系列 的QuerySet筛选方法,用于改变 QuerySet 返回的结果类型或者SQL查询执行的方式。

filter

filter(**kwargs) 
返回一个新的QuerySet,包含与给定的查询参数匹配的对象。

查找的参数(**kwargs)应该满足下文字段查找中的格式。在底层的SQL 语句中,多个参数通过AND 连接。

如果你需要执行更复杂的查询(例如,使用OR 语句查询),你可以使用Q 对象。

exclude

exclude(**kwargs) 
返回一个新的QuerySet,它包含不满足给定的查找参数的对象。

查找的参数(**kwargs)应该满足下文字段查找中的格式。 在底层的SQL 语句中,多个参数通过AND 连接,然后所有的内容放入NOT() 中。

下面的示例排除所有pub_date 晚于2005-1-3 且headline 为“Hello” 的记录:

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 或者headline 为“Hello” 的记录:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')

用SQL 语句,它等同于:

SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'

注意,第二个示例更严格。

如果你需要执行更复杂的查询(例如,使用OR 语句查询),你可以使用Q 对象。

annotate

annotate(*args, **kwargs) 
使用提供的查询表达式Annotate 查询集中的每个对象。查询表达式可以是一个简单的值、模型(或关联模型)字段的一个引用或对查询集中的对象一个聚合函数(平均值、和等)。

New in Django 1.8:
之前版本的Django 值允许聚合函数用作Annotation。现在可以使用各种表达式annotate 一个模型。
annotate() 的每个参数都是一个annotation,它将添加到返回的QuerySet 中每个对象。

Django 提供的聚合函数在下文的聚合函数文档中讲述。

关键字参数指定的Annotation 将使用关键字作为Annotation 的别名。匿名的参数的别名将基于聚合函数的名称和模型的字段生成。只有引用单个字段的聚合表达式才可以使用匿名参数。其它所有形式都必须用关键字参数。

例如,如果你正在操作一个Blog 列表,你可能想知道每个Blog 有多少Entry:

>>> 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 模型本身没有定义entry__count 属性,但是通过使用一个关键字参数来指定聚合函数,你可以控制Annotation 的名称:

>>> 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

order_by(*fields) 
默认情况下,QuerySet 根据模型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('?') 查询可能耗费资源且很慢,这取决于使用的数据库。

若要按照另外一个模型中的字段排序,可以使用查询关联模型时的语法。即通过字段的名称后面跟上两个下划线(__),再跟上新模型中的字段的名称,直至你希望连接的模型。例如:

Entry.objects.order_by('blog__name', 'headline')

如果排序的字段与另外一个模型关联,Django 将使用关联的模型的默认排序,或者如果没有指定Meta.ordering 将通过关联的模型的主键排序。 例如,因为Blog 模型没有指定默认的排序:

Entry.objects.order_by('blog')
... 等同于:

Entry.objects.order_by('blog__id')

如果Blog 设置ordering = ['name'],那么第一个QuerySet 将等同于:

Entry.objects.order_by('blog__name')

通过关联字段排序QuerySet 还能够不用带来JOIN 产生的花费,方法是引用关联字段的_id:

# No Join
Entry.objects.order_by('blog_id')

# Join
Entry.objects.order_by('blog__id')

New in Django 1.7:
QuerySet 通过关联字段进行排序不用带来JOIN 产生的开销。
你还可以通过调用表达式的asc() 或者desc(),根据查询表达式排序:

Entry.objects.order_by(Coalesce('summary', 'headline').desc())

New in Django 1.8:
增加根据查询表达式排序。
如果你还用到distinct(),在根据关联模型中的字段排序时要小心。distinct() 中有一个备注讲述关联模型的排序如何对结果产生影响。

指定一个多值字段来排序结果(例如,一个ManyToManyField 字段或者ForeignKey 字段的反向关联)。

考虑下面的情况:

class Event(Model):
   parent = models.ForeignKey('self', related_name='children')
   date = models.DateField()

Event.objects.order_by('children__date')

这里,每个Event 可能有多个潜在的排序数据;换句话说, 用 order_by()方法对 QuerySet对象进行操作会返回一个扩大版的新QuerySet对象——新增的条目也许并没有什么卵用,你也用不着它们。

因此,当你使用多值字段对结果进行排序时要格外小心。

没有方法指定排序是否考虑大小写。对于大小写的敏感性,Django 将根据数据库中的排序方式排序结果。

你可以通过Lower将一个字段转换为小写来排序,它将达到大小写一致的排序:

Entry.objects.order_by(Lower('headline').desc())

New in Django 1.8:
新增根据表达式如Lower 来排序。
如果你不想对查询做任何排序,即使是默认的排序,可以不带参数调用order_by()。

你可以通过检查QuerySet.ordered 属性来知道查询是否是排序的,如果QuerySet 有任何方式的排序它将为True。

每个order_by() 都将清除前面的任何排序。例如,下面的查询将按照pub_date 排序,而不是headline:

Entry.objects.order_by('headline').order_by('pub_date')

警告

排序不是没有开销的操作。添加到排序中的每个字段都将带来数据库的开销。添加的每个外键也都将隐式包含进它的默认排序。

reverse

reverse() 
reverse() 方法反向排序QuerySet 中返回的元素。第二次调用reverse() 将恢复到原有的排序。

如要获取QuerySet 中最后五个元素,你可以这样做:

my_queryset.reverse()[:5]
注意,这与Python 中从一个序列的末尾进行切片有点不一样。上面的例子将首先返回最后一个元素,然后是倒数第二个元素,以此类推。如果我们有一个Python 序列,当我们查看seq[-5:] 时,我们将一下子得到倒数五个元素。Django 不支持这种访问模型(从末尾进行切片),因为它不可能利用SQL 高效地实现。

同时还要注意,reverse() 应该只在一个已经定义排序的QuerySet 上调用(例如,在一个定义了默认排序的模型上,或者使用order_by() 的时候)。如果QuerySet 没有定义排序,调用reverse() 将不会有任何效果(在调用reverse() 之前没有定义排序,那么调用之后仍保持没有定义)。

distinct

distinct([*fields]) 
返回一个在SQL 查询中使用SELECT DISTINCT 的新QuerySet。它将去除查询结果中重复的行。

默认情况下,QuerySet 不会去除重复的行。在实际应用中,这一般不是个问题,因为像Blog.objects.all() 这样的简单查询不会引入重复的行。但是,如果查询跨越多张表,当对QuerySet 求值时就可能得到重复的结果。这时候你应该使用distinct()。

order_by() 调用中的任何字段都将包含在SQL 的 SELECT 列中。与distinct() 一起使用时可能导致预计不到的结果。如果你根据关联模型的字段排序,这些fields将添加到查询的字段中,它们可能产生本应该是唯一的重复的行。因为多余的列没有出现在返回的结果中(它们只是为了支持排序),有时候看上去像是返回了不明确的结果。

示例(除第一个示例外,其他示例都只能在PostgreSQL 上工作):

>>> Author.objects.distinct()
[...]

>>> Entry.objects.order_by('pub_date').distinct('pub_date')
[...]

>>> Entry.objects.order_by('blog').distinct('blog')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
[...]

>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]

values

values(*fields) 
返回一个ValuesQuerySet —— QuerySet 的一个子类,迭代时返回字典而不是模型实例对象。

每个字典表示一个对象,键对应于模型对象的属性名称。

下面的例子将values() 与普通的模型对象进行比较:

# 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 应该限制哪些字段。如果指定字段,每个字典将只包含指定的字段的键/值。如果没有指定字段,每个字典将包含数据库表中所有字段的键和值。

例如:

>>> 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 的键,因为这是保存实际的值的那个隐藏的模型属性的名称(foo 属性引用关联的模型)。当你调用values() 并传递字段的名称,传递foo 或foo_id 都可以,得到的结果是相同的(字典的键会与你传递的字段名匹配)。

例如:

>>> Entry.objects.values()
[{'blog_id': 1, 'headline': 'First Entry', ...}, ...]

>>> Entry.objects.values('blog')
[{'blog': 1}, ...]

>>> Entry.objects.values('blog_id')
[{'blog_id': 1}, ...]

当values() 与distinct() 一起使用时,注意排序可能影响最终的结果。详细信息参见distinct() 中的备注。

如果values() 子句位于extra() 调用之后,extra() 中的select 参数定义的字段必须显式包含在values() 调用中。values() 调用后面的extra() 调用将忽略选择的额外的字段。

在values() 之后调用only() 和defer() 不太合理,所以将引发一个NotImplementedError。

New in Django 1.7:
新增最后一点。以前,在values() 之后调用only() 和defer() 是允许的,但是它要么会崩溃要么返回错误的结果。
ValuesQuerySet 用于你知道你只需要字段的一小部分,而不需要用到模型实例对象的函数。只选择用到的字段当然更高效。

最后,要注意ValuesQuerySet 是QuerySet 的子类,它实现了大部分相同的方法。你可以对它调用filter()、order_by() 等等。这表示下面的两个调用完全相同:

Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()

Django 的作者喜欢将影响SQL 的方法放在前面,然后放置影响输出的方法(例如values()),但是实际上无所谓。这是卖弄你个性的好机会。

你可以通过OneToOneField、ForeignKey 和 ManyToManyField 属性反向引用关联的模型的字段:

Blog.objects.values('name', 'entry__headline')
[{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]

警告

因为ManyToManyField 字段和反向关联可能有多个关联的行,包含它们可能导致结果集的倍数放大。如果你在values() 查询中包含多个这样的字段将更加明显,这种情况下将返回所有可能的组合。

values_list

values_list(*fields, flat=False) 
与values() 类似,只是在迭代时返回的是元组而不是字典。每个元组包含传递给values_list() 调用的字段的值 —— 所以第一个元素为第一个字段,以此类推。例如:

>>> Entry.objects.values_list('id', 'headline')
[(1, '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(),它将返回模型中的所有字段,以它们在模型中定义的顺序。

注意,这个方法返回ValuesListQuerySet。这个类的行为类似列表。大部分时候它足够用了,但是如果你需要一个真实的Python 列表对象,可以对它调用list(),这将会对查询集求值。

dates

dates(field, kind, order='ASC') 

返回一个 DateQuerySet ,就是提取 QuerySet 查询中所包含的日期,将其组成一个新的 datetime.datetime 对象的列表。

field 是你的 model 中的 DateField 或 DateTimeField 字段名称。

kind 是 "year", "month" 或 "day" 之一。 每个 datetime.datetime 对象都会根据所给的 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

datetimes(field_name, kind, order='ASC', tzinfo=None) 
返回一个QuerySet 就是提取 QuerySet 查询中所包含的时间,类似dates。

none 

none() 返回一个 EmptyQuerySet -- 它是一个运行时只返回空列表的 QuerySet。它经常用在这种场合:你要返回一个空列表,但是调用者却需要接收一个 QuerySet 对象。(这时,就可以用它代替空列表)

Examples:

>>> Entry.objects.none()
[]
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True

all

all() 
返回当前QuerySet(或QuerySet 子类) 的副本。它可以用于在你希望传递一个模型管理器或QuerySet 并对结果做进一步过滤的情况。不管对哪一种对象调用all(),你都将获得一个可以工作的QuerySet。

当对QuerySet进行求值时,它通常会缓存其结果。如果数据库中的数据在QuerySet求值之后可能已经改变,你可以通过在以前求值过的QuerySet上调用相同的all() 查询以获得更新后的结果。

select_related(*fields) 
返回一个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('blog').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.utils import timezone

# Find all the blogs with entries scheduled to be published in the future.
blogs = set()

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'):
    # Without select_related(), this would make a database query for each
    # loop iteration in order to fetch the related blog for each entry.
    blogs.add(e.blog)

filter() 和select_related() 链的顺序不重要。下面的查询集是等同的:

Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())

你可以沿着外键查询。如果你有以下模型:

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('author__hometown').get(id=4) 调用将缓存关联的Person 和关联的 City:

b = Book.objects.select_related('author__hometown').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.

在传递给select_related() 的字段中,你可以使用任何ForeignKey 和OneToOneField。

在传递给select_related 的字段中,你还可以反向引用OneToOneField —— 也就是说,你可以回溯到定义OneToOneField 的字段。此时,可以使用关联对象字段的related_name,而不要指定字段的名称。

有些情况下,你希望对很多对象调用select_related(),或者你不知道所有的关联关系。在这些情况下,可以调用不带参数的select_related()。它将查找能找到的所有不可为空外键 —— 可以为空的外键必须明确指定。大部分情况下不建议这样做,因为它会使得底层的查询非常复杂并且返回的很多数据都不是真实需要的。

如果你需要清除QuerySet 上以前的select_related 添加的关联字段,可以传递一个None 作为参数:

>>> without_relations = queryset.select_related(None)

链式调用select_related 的工作方式与其它方法类似 —— 也就是说,select_related('foo', 'bar') 等同于select_related('foo').select_related('bar')。

Changed in Django 1.7:
在以前,后者等同于select_related('bar')。

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()))
and run:

>>> Pizza.objects.all()
["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...

extra

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None) 
有些情况下,Django的查询语法难以简单的表达复杂的 WHERE 子句,对于这种情况, Django 提供了 extra() QuerySet 修改机制 — 它能在 QuerySet生成的SQL从句中注入新子句

Warning

无论何时你都需要非常小心的使用extra(). 

由于产品差异的原因,这些自定义的查询难以保障在不同的数据库之间兼容(因为你手写 SQL 代码的原因),而且违背了 DRY 原则,所以如非必要,还是尽量避免写 extra。

extra可以指定一个或多个 参数,例如 select, where or tables. 这些参数都不是必须的,但是你至少要使用一个select。The select 参数可以让你在 SELECT 从句中添加其他字段信息,它应该是一个字典,存放着属性名到 SQL 从句的映射。

Example:

Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})

结果集中每个 Entry 对象都有一个额外的属性is_recent, 它是一个布尔值,表示 Entry对象的pub_date 是否晚于 Jan. 1, 2006.

Django 会直接在 SELECT 中加入对应的 SQL 片断,所以转换后的 SQL 如下:

SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent 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从句中却无须这样。

例如:

Blog.objects.extra(
    select=OrderedDict([('a', '%s'), ('b', '%s')]),
    select_params=('one', 'two'))

defer

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 的主键。(如果这样做了,虽然不会抛出错误,事实上却不完成延后加载)

注意

defer() 方法(和随后提到的 only() 方法) 都只适用于特定情况下的高级属性。它们可以提供性能上的优化,不过前提是你已经对你用到的查询有过很深入细致的分析,非常清楚你需要的究竟是哪些信息,而且已经对你所需要的数据和默认情况下返回的所有数据进行比对,清楚两者之间的差异。这完成了上述工作之后,再使用这两种方法进行优化才是有意义的。所以当你刚开始构建你的应用时,先不要急着使用 defer() 方法,等你已经写完查询并且分析成哪些方面是热点应用以后,再用也不迟。

only

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

using(alias) 
如果你使用多个数据库,这个方法用于控制QuerySet 将在哪个数据库上求值。这个方法的唯一参数是数据库的别名,定义在DATABASES。

例如:

# queries the database with the 'default' alias.
>>> Entry.objects.all()

# queries the database with the 'backup' alias
>>> Entry.objects.using('backup')

select_for_update

select_for_update(nowait=False) 

返回一个 queryset  ,会锁定相关行直到事务结束。在支持的数据库上面产生一个SELECT ... FOR UPDATE 语句

例如:

entries = Entry.objects.select_for_update().filter(author=request.user)

所有匹配的行将被锁定,直到事务结束。这意味着可以通过锁防止数据被其它事务修改。

一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。如果这不是你想要的行为,请使用select_for_update(nowait=True). 这将使查询不阻塞。如果其它事务持有冲突的锁, 那么查询将引发 DatabaseError 异常.

目前  postgresql_psycopg2, oracle 和 mysql 数据库后端 select_for_update(). 但是 MySQL 不支持 nowait 参数。显然,用户应该检查后端的支持情况。

当在不支持nowait功能的数据库后端(例如 MySql) 使用nowait=True 参数调用 select_for_update()  时将引发 DatabaseError 异常. 这是防止意外造成代码被阻塞。

在自动提交模式下使用 select_for_update() 将引发 TransactionManagementError 异常,原因是自动提交模式下不支持锁定行。如果允许这个调用,那么可能造成数据损坏,而且这个功能很容易在事务外被调用。

对于不支持 SELECT ... FOR UPDATE 的后端 (例如SQLite)  select_for_update() 将没有效果。

raw

raw(raw_query, params=None, translations=None) 
Changed in Django 1.7:
raw 移动到QuerySet 类中。以前,它只位于Manager 中。
接收一个原始的SQL 查询,执行它并返回一个django.db.models.query.RawQuerySet 实例。这个RawQuerySet 实例可以迭代以提供实例对象,就像普通的QuerySet 一样。

更多信息参见执行原始的SQL 查询。

警告

raw() 永远触发一个新的查询,而与之前的filter 无关。因此,它通常应该从Manager 或一个全新的QuerySet 实例调用。

不会返回QuerySets的方法

这些方法每次被调用的时候都会查询数据库。

get

get(**kwargs) 
返回按照查询参数匹配到的对象,参数的格式应该符合 Field lookups的要求.

如果匹配到的对象个数不只一个的话,get() 将会触发MultipleObjectsReturned 异常. MultipleObjectsReturned 异常是模型类的属性.

如果根据给出的参数匹配不到对象的话,get() 将触发DoesNotExist 异常. 这个异常是模型类的属性. Example:

Entry.objects.get(id='foo') # raises Entry.DoesNotExist
The DoesNotExist exception inherits from django.core.exceptions.ObjectDoesNotExist, so you can target multiple DoesNotExist exceptions. Example:

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

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中有一个你手动设置主键, 并且这个值已经存在于数据库中, 调用 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 将引发MultipleObjectsReturned。如果查找不到对象, get_or_create() 将会实例化并保存一个新的对象,返回一个由新的对象以及True 组成的元组。新的对象将会大概按照以下的逻辑创建:

params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update(defaults)
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 异常将会被触发。

这个方法假设正确使用原子操作,正确的数据库配置和底层数据库的正确行为。然而,如果数据库级别没有对get_or_create 中用到的kwargs 强制要求唯一性(参见unique 和 unique_together),这个方法容易导致竞态条件可能会仍具有相同参数的多行同时插入。

如果你正在使用MySQL,请确保使用READ COMMITTED 隔离级别而不是默认的REPEATABLE READ,否则你将会遇到get_or_create 引发IntegrityError 但对象在接下来的get() 调用中并不存在的情况。

最后讲一句get_or_create() 在Django 视图中的使用。请确保只在POST 请求中使用,除非你有充分的理由。GET 请求不应该对数据有任何影响。而POST 则用于对数据产生影响的请求。更多信息,参见HTTP 细则中的安全的方法。

警告

你可以通过ManyToManyField 属性和反向关联使用get_or_create()。在这种情况下,你应该限制查询在关联的上下文内部。如果你不一致地使用它,将可能导致完整性问题。

根据下面的模型:

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 的chapters 字段使用get_or_create(),但是它只会获取该Book 内部的上下文:

>>> 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 “Ulysses” 获取或者创建“Chapter 1”,但是它不能:关联关系不能获取这个chapter 因为它与这个book 不关联,但因为title 字段是唯一的它仍然不能创建。

update_or_create

update_or_create(defaults=None, **kwargs) 
New in Django 1.7.
一个通过给出的kwargs 来更新对象的便捷方法, 如果需要的话创建一个新的对象。defaults 是一个由 (field, value) 对组成的字典,用于更新对象。

返回一个由 (object, created)组成的元组,元组中的object 是一个创建的或者是被更新的对象, created 是一个标示是否创建了新的对象的布尔值。

update_or_create 方法尝试通过给出的kwargs 去从数据库中获取匹配的对象。如果找到匹配的对象,它将会依据defaults 字典给出的值更新字段。

这用作样板代码的一种快捷方式。例如:

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()

如果模型的字段数量较大的话,这种模式就变的非常不易用。上面的示例可以用 update_or_create() 重写:

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon', defaults=updated_values)

kwargs 中的名称如何解析的详细描述可以参见get_or_create()。

和上文描述的get_or_create() 一样,这个方式容易导致竞态条件,如果数据库层级没有前置唯一性它会让多行同时插入。

bulk_create

bulk_create(objs, batch_size=None) 

使用django orm大批量插入的时候我们可以不使用for循环对一个一个的save而是使用

bulk_create来批量插入

例如:

>>> Entry.objects.bulk_create([
...     Entry(headline="Django 1.0 Released"),
...     Entry(headline="Django 1.1 Announced"),
...     Entry(headline="Breaking: Django is awesome")
... ])

count

count() 
返回在数据库中对应的 QuerySet.对象的个数。 count() 永远不会引发异常。

Example:

# 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()

in_bulk

接收一个主键值列表,然后根据每个主键值所其对应的对象,返回一个主键值与对象的映射字典。

例如:

>>> 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

iterator() 

运行查询(QuerySet),然后根据结果返回一个 迭代器(iterator。 做为比较,使用 QuerySet 时,从数据库中读取所有记录后,一次性将所有记录实例化为对应的对象;而 iterator() 则是读取记录后,是分多次对数据实例化,用到哪个对象才实例化哪个对象。相对于一次性返回很多对象的 QuerySet,使用迭代器不仅效率更高,而且更节省内存。

要注意的是,如果将 iterator() 作用于 QuerySet,那就意味着会再一次运行查询,就是说会运行两次查询。

latest

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

first() 
返回结果集的第一个对象, 当没有找到时返回None.如果 QuerySet 没有设置排序,则将会自动按主键进行排序

Example:

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 提供的聚合函数在下文的聚合函数中讲述。因为聚合也是查询表达式,你可以组合多个聚合以及值来创建复杂的聚合。

使用关键字参数指定的聚合将使用关键字参数的名称作为Annotation 的名称。匿名的参数的名称将基于聚合函数的名称和模型字段生成。复杂的聚合不可以使用匿名参数,它们必须指定一个关键字参数作为别名。

例如,当你使用Blog Entry 时,你可能想知道对Author 贡献的Blog Entry 的数目:

>>> 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}

聚合的深入讨论,参见聚合的指南。

exists

exists() 
如果QuerySet 包含任何结果,则返回True,否则返回False。它会试图用最简单和最快的方法完成查询,但它执行的方法与普通的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")

它将比下面的方法快很多,这个方法要求对QuerySet 求值并迭代整个QuerySet:

if entry in some_queryset:
   print("Entry contained in QuerySet")

若要查找一个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")

... 但不会快很多(因为这需要很大的QuerySet 以获得效率的提升)。

另外,如果some_queryset 还没有求值,但你知道它将在某个时刻求值,那么使用some_queryset.exists() 将比简单地使用bool(some_queryset) 完成更多的工作(一个查询用于存在性检查,另外一个是后面的求值),后者将求值并检查是否有结果返回。

update

update(**kwargs) 

更新操作

例如:

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)

更新特定的数据

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')

不能对外键进行更新操作

>>> Entry.objects.update(blog__name='foo') # Won't work!

可以通过外键进行更新

>>> Entry.objects.filter(blog__id=1).update(comments_on=True)

下面是更新的变种方法

e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

上面的语句类似下面的语句:

Entry.objects.filter(id=10).update(comments_on=False)

delete 

delete() 
删除操作

例如:

>>> b = Blog.objects.get(pk=1)

删除所有满足条件的

>>> Entry.objects.filter(blog=b).delete()

上面操作类似于下面语句:

blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
blogs.delete()

as_manager

classmethod as_manager() 
New in Django 1.7.
类方法返回一个管理器实例。

字段查找

字段查询是指如何指定SQL WHERE子句的内容. 它们通过查询集的filter(), exclude() and get()的关键字参数指定.

exact

精确匹配。

Examples:

Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)
SQL equivalents:

SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;

iexact

不区分大小写的精确匹配
例如:

Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)
SQL equivalents:

SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;

contains

模糊匹配.

例如:

Entry.objects.get(headline__contains='Lennon')
SQL equivalent:

SELECT ... WHERE headline LIKE '%Lennon%';

icontains

不区分大小写的模糊匹配

例如:

Entry.objects.get(headline__icontains='Lennon')
SQL equivalent:

SELECT ... WHERE headline ILIKE '%Lennon%';

in

相当于数据库的in查询.

例如:

Entry.objects.filter(id__in=[1, 3, 4])
SQL equivalent:

SELECT ... WHERE id IN (1, 3, 4);

你也可以动态生成list进行in查询:

inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)

上面的语句类似于下面的sql语句:

SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')

如果使用ValuesQuerySet 查询,确保只选择一个列:

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)

gt

大于

例子:

Entry.objects.filter(id__gt=4)
SQL语句相当于:

SELECT ... WHERE id > 4;

gte

大于或等于

lt

小于

lte

小于或等于

startswith

区分大小写,开始位置匹配

例如:

Entry.objects.filter(headline__startswith='Will')
SQL equivalent:

SELECT ... WHERE headline LIKE 'Will%';

SQLite 不支持区分大小写 LIKE 语句; Sqlite 下startswith 等于 istartswith .

istartswith

不区分大小写,开始位置匹配

例如:

Entry.objects.filter(headline__istartswith='will')
SQL equivalent:

SELECT ... WHERE headline ILIKE 'Will%';

endswith

以什么结尾.

例如:

Entry.objects.filter(headline__endswith='cats')
SQL equivalent:

SELECT ... WHERE headline LIKE '%cats';

iendswith

以什么结尾不区分大小写.

例如:

Entry.objects.filter(headline__iendswith='will')
SQL equivalent:

SELECT ... WHERE headline ILIKE '%will'

range

范围查询,类似于sql的between and.

Example:

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 equivalent:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

year

对日期/时间字段精确匹配年分,年分用四位数字表示。

例如:

Entry.objects.filter(pub_date__year=2005)

等价于 SQL:

SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005';

(不同的数据库引擎中,翻译得到的 SQL 也不尽相同。)

month

对日期/时间字段精确匹配月分,用整数表示月分,比如 1 表示一月,12 表示十二月。

例如:

Entry.objects.filter(pub_date__month=12)

等价于 SQL:

SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';

(不同的数据库引擎中,翻译得到的 SQL 也不尽相同。)

day

对于日期和日期时间字段,具体到某一天的匹配。取一个整数的天数。

Example:

Entry.objects.filter(pub_date__day=3)
SQL equivalent:

SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';

week_day

对日期/时间字段匹配星期几

例如:

Entry.objects.filter(pub_date__week_day=2)

等价于 SQL:

SELECT ... WHERE EXTRACT('dow' FROM pub_date) = '2';

(不同的数据库引擎中,翻译得到的 SQL 也不尽相同。)

要注意的是,这段代码将得到 pub_date 字段是星期一的所有记录 (西方习惯于将星期一看做一周的第二天),与它的年月信息无关。星期以星期天做为第一天,以星期六做为最后一天。

hour

对于小时的匹配,取值只能是0到23之间.

例如:

Event.objects.filter(timestamp__hour=23)
SQL equivalent:

SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';

minute

对于分钟的匹配,取值只能是0到59之间.

例如:

Event.objects.filter(timestamp__minute=29)
SQL equivalent:

SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';

second

对于秒的匹配,取值只能是0到59之间.

例如:

Event.objects.filter(timestamp__second=31)
等同于SQL语句:

SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';

isnull

值为 True 或 False, 相当于 SQL语句IS NULL和IS NOT NULL.

例如:

Entry.objects.filter(pub_date__isnull=True)
SQL equivalent:

SELECT ... WHERE pub_date IS NULL;

一个Boolean类型的全文搜索,以全文搜索的优势。这个很像 contains ,但是由于全文索引的优势,以使它更显著的快。

Example:

Entry.objects.filter(headline__search="+Django -jazz Python")
SQL equivalent:

SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);

regex

正则表达式的匹配规则

例如:

Entry.objects.get(title__regex=r'^(An?|The) +')
SQL equivalents:

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

iregex

正则表达式的匹配规则,忽略大小写

例如:

Entry.objects.get(title__iregex=r'^(an?|the) +')
SQL equivalents:

SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle

SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite

聚合函数

Django 的django.db.models 模块提供以下聚合函数。关于如何使用这些聚合函数的细节,参见聚合函数的指南。关于如何创建聚合函数,参数聚合函数 的文档。

警告

SQLite 不能直接处理日期/时间字段的聚合。这是因为SQLite 中没有原生的日期/时间字段,Django 目前使用文本字段模拟它的功能。在SQLite 中对日期/时间字段使用聚合将引发NotImplementedError。

在QuerySet 为空时,聚合函数函数将返回None。 例如,如果QuerySet 中没有记录,Sum 聚合函数将返回None 而不是0。Count 是一个例外,如果QuerySet 为空,它将返回0。

所有聚合函数具有以下共同的参数:

expression

引用模型字段的一个字符串,或者一个查询表达式。

New in Django 1.8:
现在在复杂的计算中,聚合函数可以引用多个字段。

output_field 

用来表示返回值的模型字段,它是一个可选的参数。

New in Django 1.8:
添加output_field 参数。

在组合多个类型的字段时,只有在所有的字段都是相同类型的情况下,Django 才能确定output_field。否则,你必须自己提供output_field 参数。

**extra

这些关键字参数可以给聚合函数生成的SQL 提供额外的信息。

Avg

class Avg(expression, output_field=None, **extra) 
返回给定expression 的平均值,其中expression 必须为数值。

默认的别名:__avg
返回类型:float

Count 

class Count(expression, distinct=False, **extra) 
返回与expression 相关的对象的个数。

默认的别名:__count
返回类型:int
有一个可选的参数:distinct 如果distinct=True,Count 将只计算唯一的实例。它等同于COUNT(DISTINCT ) SQL 语句。默认值为False。

Max

class Max(expression, output_field=None, **extra) 
返回expression 的最大值。

默认的别名:__max
返回类型:与输入字段的类型相同,如果提供则为 output_field 类型

Min 

class Min(expression, output_field=None, **extra) 
返回expression 的最小值。

默认的别名:__min
返回的类型:与输入字段的类型相同,如果提供则为 output_field 类型

StdDev 

class StdDev(expression, sample=False, **extra) 
返回expression 的标准差。

默认的别名:__stddev
返回类型:float
有一个可选的参数:

sample 
默认情况下,StdDev 返回群体的标准差。但是,如果sample=True,返回的值将是样本的标准差。

SQLite

SQLite 没有直接提供StdDev。有一个可用的实现是SQLite 的一个扩展模块。参见SQlite 的文档 中获取并安装这个扩展的指南。

Sum

class Sum(expression, output_field=None, **extra) 
计算expression 的所有值的和。

默认的别名:__sum
返回类型:与输入的字段相同,如果提供则为output_field 的类型

Variance 

class Variance(expression, sample=False, **extra) 
返回expression 的方差。

默认的别名:__variance
返回的类型:float
有一个可选的参数:sample 默认情况下,Variance 返回群体的方差。但是,如果sample=True,返回的值将是样本的方差。

SQLite

SQLite 没有直接提供Variance。有一个可用的实现是SQLite 的一个扩展模块。 参见SQlite 的文档 中获取并安装这个扩展的指南。

查询相关的类

本节提供查询相关的工具的参考资料,它们其它地方没有文档。

Q() 对象

class Q 
Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询。

一般我们在Django程序中查询数据库操作都是在QuerySet里进行进行,例如下面代码:

>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())

或者将其组合起来,例如:

>>>q1 = Entry.objects.filter(headline_startswith="What").exclude(pub_date_gte=datetime.date.today())

随着我们的程序越来越复杂,查询的条件也跟着复杂起来,这样简单的通过一个filter()来进行查询的条件将导致我们的查询越来越长。

Q()对象就是为了将这些条件组合起来。

当我们在查询的条件中需要组合条件时(例如两个条件“且”或者“或”)时。我们可以使用Q()查询对象。例如下面的代码

fromdjango.db.modelsimports Q
q=Q(question_startswith="What")

这样就生成了一个Q()对象,我们可以使用符号&或者|将多个Q()对象组合起来传递给filter(),exclude(),get()等函数。当多个Q()对象组合起来时,Django会自动生成一个新的Q()。例如下面代码就将两个条件组合成了一个

Q(question__startswith='Who') | Q(question__startswith='What')

 使用上述代码可以使用SQL语句这么理解:

WHEREquestionLIKE 'Who%' ORquestionLIKE 'What%'

我们可以在Q()对象的前面使用字符“~”来代表意义“非”,例如下面代码:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

对应SQL语句可以理解为:

WHEREquestionlike "Who%" ORyear(pub_date) !=2005

这样我们可以使用 “&”或者“|”还有括号来对条件进行分组从而组合成更加复杂的查询逻辑。

也可以传递多个Q()对象给查询函数,例如下面代码:

News.objects,get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

 多个Q()对象之间的关系Django会自动理解成“且(and)”关系。如上面代码使用SQL语句理解将会是:

SELECT * fromnewsWHEREquestionLIKE 'Who%'  AND (pub_date = '2005-05-02' ORpub_date = '2005-05-06')

Q()对象可以结合关键字参数一起传递给查询函数,不过需要注意的是要将Q()对象放在关键字参数的前面,看下面代码

#正确的做法

News.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who')

#错误的做法,代码将关键字参数放在了Q()对象的前面。

News.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这