Skip to main content

QuerySet API参考

本文档描述了 QuerySet API的详细信息。它基于 模型数据库查询 指南中提供的材料,因此您可能希望在阅读这些文档之前阅读并理解这些文档。

在本参考文献中,我们将使用 数据库查询指南 中提出的 示例Weblog模型

当评估 QuerySet

在内部,QuerySet 可以被构造,过滤,切片,并且通常传递而不实际击中数据库。在您执行某些操作来评估查询集之前,实际上不会发生数据库活动。

您可以通过以下方式评估 QuerySet

  • 迭代。 QuerySet 是可迭代的,并且在第一次迭代它时执行其数据库查询。例如,这将打印数据库中所有条目的标题:

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

    注意:如果您只想确定是否至少存在一个结果,请不要使用此选项。使用 exists() 更有效。

  • 切片。限制 QuerySet 中所述,可以使用Python的数组切片语法对 QuerySet 进行切片。切分未经评估的 QuerySet 通常返回另一个未经评估的 QuerySet,但如果使用slice语法的“step”参数,Django将执行数据库查询,并返回一个列表。对已评估的 QuerySet 进行切片也会返回列表。

    还要注意,即使切分未经评估的 QuerySet 返回另一个未经评估的 QuerySet,也不允许进一步修改它(例如,添加更多过滤器或修改排序),因为它不能很好地转换为SQL,也不会有明确的含义。

  • 酸洗/缓冲。 有关 pickling QuerySets 所涉及的详细信息,请参阅以下部分。对于本节的目的,重要的是从数据库读取结果。

  • repr()。 当你调用 repr() 时,QuerySet 被评估。这是为了方便Python交互式解释器,所以当以交互方式使用API时,您可以立即看到您的结果。

  • len()。 当你调用 len() 时,QuerySet 被评估。这可能会返回结果列表的长度。

    注意:如果只需要确定集合中的记录数(并且不需要实际的对象),使用SQL的 SELECT COUNT(*) 在数据库级别处理计数会更加高效。正因为这个原因,Django提供了 count() 方法。

  • list()。 通过调用 list()QuerySet 进行强制评估。例如:

    entry_list = list(Entry.objects.all())
    
  • bool()。 在布尔上下文中测试 QuerySet,例如使用 bool()orandif 语句,将导致执行查询。如果有至少一个结果,则 QuerySetTrue,否则是 False。例如:

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

    注意:如果只想确定是否至少存在一个结果(并且不需要实际对象),使用 exists() 更有效。

酸洗 QuerySet

如果 pickleQuerySet,这将强制所有的结果在酸洗之前加载到内存中。 Pickling通常用作缓存的前身,并且当重新加载缓存的查询集时,您希望结果已经存在并准备使用(从数据库读取可能需要一些时间,从而违反缓存的目的)。这意味着,当取消 QuerySet 取消启动时,它包含当前被pickled的时候的结果,而不是当前在数据库中的结果。

如果您只想选择必要的信息以在稍后从数据库重新创建 QuerySet,请选择 QuerySetquery 属性。然后,您可以使用这样的代码重新创建原始的 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 属性是一个不透明的对象。它代表查询构造的内部并且不是公共API的一部分。但是,如下所述,安全(并完全支持)选择和取消选择属性的内容。

你不能在版本之间共享泡菜

Pickles的 QuerySets 只对用于生成它们的Django的版本有效。如果你使用Django版本N生成一个pickle,不能保证pickle可以用Django版本N + 1读取。酱菜不应该用作长期档案策略的一部分。

由于pickle兼容性错误可能很难诊断,例如静默损坏的对象,当您尝试取消与Django版本中的查询集不同的查询集时,会引发 RuntimeWarning

QuerySet API

这是 QuerySet 的正式声明:

class QuerySet(model=None, query=None, using=None)[源代码]

通常当你与 QuerySet 交互时,你将使用 链接过滤器。为了使这个工作,大多数 QuerySet 方法返回新的查询集。这些方法将在本节后面详细介绍。

QuerySet 类有两个公共属性,您可以用于内省:

ordered

True,如果 QuerySet 是有序的 - 即在模型上具有 order_by() 子句或默认排序。 False

db

如果此查询现在执行,将使用的数据库。

注解

存在 query 参数到 QuerySet,使得诸如 GeoQuerySet 的专用查询子类可以重构内部查询状态。参数的值是该查询状态的不透明表示,不是公共API的一部分。简单地说:如果你需要问,你不需要使用它。

返回新 QuerySet 的方法

Django提供了一系列 QuerySet 细化方法,可以修改 QuerySet 返回的结果类型或执行SQL查询的方式。

filter()

filter(**kwargs)

返回包含与给定查找参数匹配的对象的新 QuerySet

查找参数(**kwargs)应采用下面 Field lookups 中描述的格式。多个参数通过底层SQL语句中的 AND 连接。

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

exclude()

exclude(**kwargs)

返回一个新的 QuerySet,它包含与给定查找参数匹配的 not 对象。

查找参数(**kwargs)应采用下面 Field lookups 中描述的格式。多个参数通过基础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 OR或其标题为“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 objects

annotate()

annotate(*args, **kwargs)

使用提供的 查询表达式 列表在 QuerySet 中注释每个对象。表达式可以是简单值,对模型上的字段的引用(或任何相关模型),或者是在与对象相关的对象上计算的聚合表达式(平均值,求和等) QuerySet

annotate() 的每个参数是将添加到返回的 QuerySet 中的每个对象的注释。

Django提供的聚合函数在下面的 Aggregation Functions 中描述。

使用关键字参数指定的注释将使用关键字作为注释的别名。匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成别名。只有引用单个字段的聚合表达式可以是匿名参数。其他都必须是关键字参数。

例如,如果您正在处理博客列表,则可能需要确定在每个博客中创建了多少条目:

>>> 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 属性,但是通过使用关键字参数指定聚合函数,可以控制注释的名称:

>>> 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" 前面的负号表示 descending 顺序。暗示升序。要随机订购,使用 "?",像这样:

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

如果 Blogordering = ['name'],那么第一查询集将是相同的:

Entry.objects.order_by('blog__name')

还可以通过相关字段来排序查询集,而不会引起JOIN的成本,通过参考相关字段的 _id:

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

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

您还可以通过在表达式上调用 asc()desc() 来由 查询表达式 订购:

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

如果您也使用 distinct(),在相关模型中的字段排序时要小心。参见 distinct() 中的说明,了解相关模型排序如何改变预期结果。

注解

允许指定多值字段以通过(例如,ManyToManyField 字段或 ForeignKey 字段的反向关系)对结果排序。

考虑这种情况:

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

Event.objects.order_by('children__date')

这里,每个 Event 可能有多个排序数据;每个具有多个 childrenEvent 将被多次返回到 order_by() 创建的新 QuerySet 中。换句话说,在 QuerySet 上使用 order_by() 可以返回比开始处理的项目更多的项目 - 这可能既不是预期的也不是有用的。

因此,在使用多值字段排序结果时要小心。 如果,您可以确保每个订单的项目只有一个订购数据,这种方法不应该出现问题。如果没有,确保结果是你期望的。

没有办法指定排序是否区分大小写。对于区分大小写,Django会命令结果,但是你的数据库后端通常会订购它们。

您可以通过使用 Lower 转换为小写的字段进行排序,这将实现大小写一致的排序:

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

如果不想对查询应用任何顺序,甚至不使用默认顺序,请调用没有参数的 order_by()

您可以通过检查 QuerySet.ordered 属性来判断查询是否排序,如果 QuerySet 已以任何方式排序,则该属性将是 True

每个 order_by() 呼叫将清除任何先前的顺序。例如,此查询将由 pub_date 而不是 headline 排序:

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

警告

订购不是免费操作。添加到排序中的每个字段都会对数据库产生成本。您添加的每个外键也将隐含地包括其所有默认排序。

如果查询未指定排序,则以未指定的顺序从数据库返回结果。只有当通过唯一地标识结果中的每个对象的一组字段进行排序时,才保证特定的排序。例如,如果 name 字段不是唯一的,则它的排序不会保证具有相同名称的对象总是以相同的顺序出现。

reverse()

reverse()

使用 reverse() 方法来反转查询集的元素返回的顺序。第二次调用 reverse() 将顺序恢复为正常方向。

要检索查询集中的“最后”五个项目,您可以这样做:

my_queryset.reverse()[:5]

注意,这和从Python中的序列末尾切片不一样。上面的示例将返回最后一个项目,然后返回倒数第二个项目,依此类推。如果我们有一个Python序列并查看 seq[-5:],我们将首先看到第五个最后一个项目。 Django不支持这种访问模式(从末尾切分),因为它不可能在SQL中有效地执行。

另外,注意,reverse() 通常只应当在具有定义的排序(例如,当针对定义默认排序的模型进行查询时,或者当使用 order_by() 时)的 QuerySet 上被调用。如果没有为给定的 QuerySet 定义这样的排序,则对其调用 reverse() 没有实际效果(在调用 reverse() 之前排序是未定义的,并且在之后将保持未定义)。

distinct()

distinct(*fields)

返回在其SQL查询中使用 SELECT DISTINCT 的新 QuerySet。这会从查询结果中删除重复的行。

默认情况下,QuerySet 不会删除重复行。在实践中,这很少是一个问题,因为简单的查询如 Blog.objects.all() 不会引入重复结果行的可能性。但是,如果查询跨多个表,则可能在评估 QuerySet 时获取重复的结果。这是当你使用 distinct()

注解

order_by() 调用中使用的任何字段都包含在SQL SELECT 列中。当与 distinct() 一起使用时,这有时可能导致意外的结果。如果按相关模型中的字段排序,那些字段将添加到选定的列,并且它们可能使其他重复的行显得不同。由于额外的列不会出现在返回的结果中(它们只是为了支持排序),它有时看起来像返回非不同的结果。

同样,如果使用 values() 查询来限制所选的列,则任何 order_by() (或默认模型排序)中使用的列仍将涉及,并可能影响结果的唯一性。

这里的道德是,如果你使用 distinct() 小心有关的模型排序。类似地,当一起使用 distinct()values() 时,在不是在 values() 调用中的字段排序时要小心。

在PostgreSQL上,您可以传递位置参数(*fields),以便指定 DISTINCT 应该应用的字段的名称。这转换为 SELECT DISTINCT ON SQL查询。这里有区别。对于正常的 distinct() 调用,数据库在确定哪些行不同时比较每行中的 each 字段。对于具有指定字段名称的 distinct() 调用,数据库将仅比较指定的字段名称。

注解

当您指定字段名称时,mustQuerySet 中提供 order_by()order_by() 中的字段必须以 distinct() 中的字段开头,顺序相同。

例如,SELECT DISTINCT ON (a) 为列 a 中的每个值提供第一行。如果你不指定一个顺序,你会得到一个任意行。

示例(那些之后的第一个将只适用于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')
[...]

注解

请记住,order_by() 使用已定义的任何默认相关模型排序。您可能必须通过关系 _id 或引用字段显式排序,以确保 DISTINCT ON 表达式与 ORDER BY 子句开头处的表达式匹配。例如,如果 Blog 模型由 name 定义 ordering:

Entry.objects.order_by('blog').distinct('blog')

...将不工作,因为查询将由 blog__name 排序,从而使 DISTINCT ON 表达式不匹配。您必须通过关系 _id 字段(在这种情况下为 blog_id)或引用的一个(blog__pk)显式排序,以确保两个表达式匹配。

values()

values(*fields)

返回一个 QuerySet,当用作可迭代时返回字典,而不是模型实例。

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

此示例将 values() 的字典与正常模型对象进行比较:

# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

values() 方法接受可选的位置参数 *fields,它指定了 SELECT 应该限制的字段名称。如果指定字段,则每个字典将仅包含您指定的字段的字段键/值。如果不指定字段,则每个字典将包含数据库表中每个字段的键和值。

例:

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

值得一提的一些细微之处:

  • 如果您有一个名为 foo 的字段是 ForeignKey,则默认的 values() 调用将返回一个名为 foo_id 的字典键,因为这是存储实际值的隐藏模型属性的名称(foo 属性是指相关模型)。当你调用 values() 并传入字段名时,你可以传入 foofoo_id,你将得到相同的东西(字典键将匹配你传递的字段名)。

    例如:

    >>> Entry.objects.values()
    <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
    
    >>> Entry.objects.values('blog')
    <QuerySet [{'blog': 1}, ...]>
    
    >>> Entry.objects.values('blog_id')
    <QuerySet [{'blog_id': 1}, ...]>
    
  • 当与 distinct() 一起使用 values() 时,请注意,排序可能会影响结果。有关详细信息,请参阅 distinct() 中的注释。

  • 如果在 extra() 调用之后使用 values() 子句,extra() 中的 select 参数定义的任何字段必须明确包括在 values() 调用中。在 values() 呼叫之后进行的任何 extra() 呼叫将忽略其额外选择的字段。

  • values() 之后调用 only()defer() 没有意义,因此这样做会产生 NotImplementedError

它是有用的,当你知道你只需要从一小部分可用字段的值,你不需要模型实例对象的功能。更有效地只选择您需要使用的字段。

最后,请注意,您可以在 values() 调用之后调用 filter()order_by() 等,这意味着这两个调用是相同的:

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

使用Django的人喜欢首先放置所有影响SQL的方法,然后(可选地)使用任何影响输出的方法(如 values()),但这并不重要。这是你真正炫耀你的个人主义的机会。

您还可以通过 OneToOneFieldForeignKeyManyToManyField 属性引用具有反向关系的相关模型上的字段:

>>> Blog.objects.values('name', 'entry__headline')
<QuerySet [{'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() 传递任何值,它将按照声明的顺序返回模型中的所有字段。

常见的需求是获取某个模型实例的特定字段值。为了实现这一点,使用 values_list() 后跟一个 get() 调用:

>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'

values()values_list() 都旨在作为特定用例的优化:检索数据子集,而不需要创建模型实例的开销。当处理多对多和其他多值关系(例如反向外键的一对多关系)时,这个隐喻分裂,因为“一行,一个对象”假设不成立。

例如,注意在跨 ManyToManyField 查询时的行为:

>>> Author.objects.values_list('name', 'entry__headline')
[('Noam Chomsky', 'Impressions of Gaza'),
 ('George Orwell', 'Why Socialists Do Not Believe in Fun'),
 ('George Orwell', 'In Defence of English Cooking'),
 ('Don Quixote', None)]

具有多个条目的作者多次出现,没有任何条目的作者对于条目标题具有 None

类似地,当查询反向外键时,None 出现用于没有任何作者的条目:

>>> Entry.objects.values_list('authors')
[('Noam Chomsky',), ('George Orwell',), (None,)]

dates()

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

返回 QuerySet,该 QuerySet 评估为表示 QuerySet 内容中特定种类的所有可用日期的 datetime.date 对象列表。

field 应该是您的模型的 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()

datetimes(field_name, kind, order='ASC', tzinfo=None)

返回 QuerySet,该 QuerySet 评估为表示 QuerySet 内容中特定种类的所有可用日期的 datetime.datetime 对象列表。

field_name 应该是您的模型的 DateTimeField 的名称。

kind 应为 "year""month""day""hour""minute""second"。结果列表中的每个 datetime.datetime 对象都“截断”到给定的 type

order,默认为 'ASC',应为 'ASC''DESC'。这指定如何排序结果。

tzinfo 定义在截断之前将数据时间转换到的时区。事实上,给定的datetime具有不同的表示,这取决于使用的时区。此参数必须是 datetime.tzinfo 对象。如果是 None,Django使用 当前时区。当 USE_TZFalse 时,它没有效果。

注解

此函数直接在数据库中执行时区转换。因此,您的数据库必须能够解释 tzinfo.tzname(None) 的值。这转化为以下要求:

none()

none()

调用none()将创建一个查询集,它不会返回任何对象,并且在访问结果时不会执行任何查询。 qs.none()查询集是 EmptyQuerySet 的一个实例。

例子:

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

all()

all()

返回当前 QuerySet (或 QuerySet 子类)的 copy。在您可能需要传入模型管理器或 QuerySet 并对结果进行进一步过滤的情况下,这可能非常有用。在任何对象上调用 all() 之后,你肯定会有一个 QuerySet 来使用。

QuerySet评价 时,它通常缓存其结果。如果自评估 QuerySet 以来数据库中的数据可能已更改,则可以通过在先前评估的 QuerySet 上调用 all() 来获取同一查询的更新结果。

extra()

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

有时,Django查询语法本身不能轻易表达复杂的 WHERE 子句。对于这些边缘情况,Django提供了 extra() QuerySet 修饰符 - 一个用于向 QuerySet 生成的SQL中注入特定子句的钩子。

使用此方法作为最后手段

这是一个旧的API,我们的目标是在将来的某个时候弃用。仅当您无法使用其他查询集方法表达查询时,才使用它。如果你确实需要使用它,请 文件票 使用 QuerySet.extra关键字 与您的用例(请首先检查现有票证的列表),以便我们可以增强QuerySet API允许删除 extra()。我们不再改进或修复此方法的错误。

例如,这种使用 extra():

>>> qs.extra(
...     select={'val': "select col from sometable where othercol = %s"},
...     select_params=(someparam,),
... )

相当于:

>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))

使用 RawSQL 的主要好处是您可以根据需要设置 output_field。主要缺点是,如果你引用原始SQL中查询集的某些表别名,那么Django可能会更改该别名(例如,当查询集在另一个查询中用作子查询时)。

警告

当你使用 extra() 时你应该非常小心。每次使用它,你应该逃避任何参数,用户可以通过使用 params 控制,以防止SQL注入攻击。请阅读更多关于 SQL注入保护

根据定义,这些额外的查找可能不能移植到不同的数据库引擎(因为你明确地写SQL代码),违反了DRY原则,所以你应该尽可能避免它们。

指定 paramsselectwheretables 中的一个或多个。不需要任何参数,但应至少使用其中一个。

  • select

    select 参数允许您在 SELECT 子句中输入额外的字段。它应该是将属性名称映射到SQL子句的字典,用于计算该属性。

    例:

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

    因此,每个 Entry 对象将具有额外的属性 is_recent,该布尔表示条目的 pub_date 是否大于2006年1月1日。

    Django将给定的SQL代码段直接插入到 SELECT 语句中,因此上面示例的结果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'
        },
    )
    

    在这种特殊情况下,我们利用了一个事实,即查询已经在其 FROM 子句中包含了 blog_blog 表。

    上面例子的结果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 属性是一个字典,需要注意,以便参数与额外的选择片段正确匹配。在这种情况下,您应该使用 collections.OrderedDict 作为 select 值,而不仅仅是一个普通的Python字典。

    这将工作,例如:

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

    如果需要在选择字符串中使用文字 %s,请使用序列 %%s

  • where / tables

    您可以通过使用 where 来定义明确的SQL WHERE 子句 - 可能执行非显式连接。您可以使用 tables 手动将表添加到SQL FROM 子句。

    wheretables 都接受字符串列表。所有 where 参数均与任何其他搜索条件“AND”。

    例:

    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    

    ...翻译(粗略地)到以下SQL:

    SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')
    

    如果要指定已在查询中使用的表,请在使用 tables 参数时小心。当您通过 tables 参数添加额外的表时,Django假定您希望该表包含额外的时间(如果已包括)。这会产生一个问题,因为表名将会被赋予一个别名。如果表在SQL语句中多次出现,则第二次和后续出现必须使用别名,以便数据库可以区分它们。如果你指的是在额外的 where 参数中添加的额外表,这将导致错误。

    通常,您只需添加尚未显示在查询中的额外表。然而,如果发生上述情况,有几种解决方案。首先,看看你是否可以不包括额外的表,并使用已经在查询中的一个。如果这不可能,将您的 extra() 调用放在查询集结构的前面,这样您的表是该表的第一次使用。最后,如果所有其他失败,查看生成的查询并重写您的 where 添加以使用给您的额外表的别名。每次以相同的方式构造查询集时,别名将是相同的,因此您可以依赖别名不更改。

  • order_by

    如果您需要使用通过 extra() 包含的一些新字段或表来订购生成的查询集,请使用 order_by 参数到 extra() 并传递字符串序列。这些字符串应该是模型字段(如在查询集上的常规 order_by() 方法中),格式为 table_name.column_name,或者是在 select 参数中指定为 extra() 的列的别名。

    例如:

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

    这将将 is_recent 为真的所有项目排序到结果集的前面(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'])
    

警告

如果您正在对MySQL执行查询,请注意,MySQL的静默类型强制可能会在混合类型时导致意外的结果。如果查询字符串类型列,但使用整数值,MySQL将在执行比较之前将表中所有值的类型强制为整数。例如,如果表包含值 'abc''def' 并且您查询 WHERE mycolumn=0,则两行都将匹配。为了防止出现这种情况,请在使用查询中的值之前执行正确的类型转换。

defer()

defer(*fields)

在一些复杂的数据建模情况下,您的模型可能包含大量字段,其中一些可能包含大量数据(例如,文本字段),或者需要昂贵的处理来将它们转换为Python对象。如果您在某些情况下使用查询集的结果,当您最初获取数据时不知道是否需要这些特定字段,可以告诉Django不要从数据库中检索它们。

这是通过传递字段的名称不加载到 defer() 完成:

Entry.objects.defer("headline", "body")

具有延迟字段的查询集仍将返回模型实例。如果您访问该字段(一次一个,而不是一次所有的延迟字段),则将从数据库中检索每个延迟字段。

您可以多次呼叫 defer()。每个调用都向延迟集添加新字段:

# Defers both the body and headline fields.
Entry.objects.defer("body").filter(rating=5).defer("headline")

字段添加到延迟集的顺序无关紧要。调用已经延迟的字段名称的 defer() 是无害的(字段仍将被延迟)。

您可以通过使用标准的双下划线符号来分隔相关字段,推迟相关模型中字段的加载(如果相关模型通过 select_related() 加载):

Blog.objects.select_related().defer("entry__headline", "entry__body")

如果要清除延迟字段集,请将 None 作为参数传递给 defer():

# Load all fields immediately.
my_queryset.defer(None)

模型中的某些字段不会被延迟,即使您要求它们。您永远不能推迟加载主键。如果使用 select_related() 检索相关模型,则不应推迟从主模型连接到相关模型的字段的加载,否则将导致错误。

注解

defer() 方法(及其表弟,only(),以下)仅用于高级用例。当您仔细分析查询并了解 exactly 需要哪些信息时,他们可以提供优化,并且衡量返回所需字段和模型的完整字段之间的区别将是重要的。

即使你认为你是在高级用例的情况下,只有当你不能在查询集加载时使用defer(),确定你是否需要额外的字段。如果您经常加载和使用特定的数据子集,最好的选择是规范化模型,并将未加载的数据放入单独的模型(和数据库表)。如果列 must 由于某种原因停留在一个表中,请使用 Meta.managed = False (请参阅 managed attribute 文档)创建一个仅包含通常需要加载的字段的模型,并在那里调用 defer()。这使得你的代码对读者更加明确,稍微更快,在Python进程中消耗更少的内存。

例如,这两个模型使用相同的底层数据库表:

class CommonlyUsedModel(models.Model):
    f1 = models.CharField(max_length=10)

    class Meta:
        managed = False
        db_table = 'app_largetable'

class ManagedModel(models.Model):
    f1 = models.CharField(max_length=10)
    f2 = models.CharField(max_length=10)

    class Meta:
        db_table = 'app_largetable'

# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.all().defer('f2')

如果许多字段需要在非托管模型中复制,最好使用共享字段创建抽象模型,然后使非托管模型和托管模型从抽象模型继承。

注解

当为具有延迟字段的实例调用 save() 时,将仅保存加载的字段。有关详细信息,请参阅 save()

only()

only(*fields)

only() 方法或多或少与 defer() 相反。您可以使用应该在检索模型时推迟 not 的字段来调用它。如果你有一个模型几乎所有的字段需要延迟,使用 only() 指定字段的互补集可以导致更简单的代码。

假设你有一个具有字段 nameagebiography 的模型。以下两个查询集是相同的,就延迟字段而言:

Person.objects.defer("age", "biography")
Person.objects.only("name")

每当你调用 only()replaces 的一组字段立即加载。方法的名称是助记符:只要 这些字段立即加载;其余的都被推迟。因此,对 only() 的连续调用仅导致考虑的最后字段:

# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")

由于 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")

defer() 文档注释中的所有注意事项也适用于 only()。使用它谨慎,只有耗尽了你的其他选项。

使用 only() 并省略使用 select_related() 请求的字段也是错误。

注解

当为具有延迟字段的实例调用 save() 时,将仅保存加载的字段。有关详细信息,请参阅 save()

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)

返回一个查询集,它将锁定行,直到事务结束,在支持的数据库上生成 SELECT ... FOR UPDATE SQL语句。

例如:

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

所有匹配的条目将被锁定,直到事务块结束,意味着将防止其他事务改变或获取它们上的锁。

通常,如果另一个事务已经在所选行中的一个上获取了锁,则查询将阻塞,直到锁被释放。如果这不是你想要的行为,请调用 select_for_update(nowait=True)。这将使呼叫无阻塞。如果冲突的锁已经被另一个事务获取,则在评估查询集时将引发 DatabaseError

目前,postgresqloraclemysql 数据库后端支持 select_for_update()。但是,MySQL不支持 nowait 参数。很明显,外部第三方后端的用户应该在后台文档中检查这些情况下的详细信息。

使用不支持 nowait 的数据库后端(例如MySQL)将 nowait=True 传递到 select_for_update() 将导致引发 DatabaseError。这是为了防止代码意外阻塞。

在支持 SELECT ... FOR UPDATE 的后端以自动提交模式使用 select_for_update() 评估查询集是一个 TransactionManagementError 错误,因为在这种情况下行不锁定。如果允许,这将有助于数据损坏,并且很容易由调用预期在一个事务外部运行的代码引起。

在不支持 SELECT ... FOR UPDATE 的后端(例如SQLite)上使用 select_for_update() 将没有任何效果。 SELECT ... FOR UPDATE 不会添加到查询中,如果在自动提交模式下使用 select_for_update(),则不会引发错误。

警告

虽然 select_for_update() 通常在自动提交模式下失败,因为 TestCase 在事务中自动包装每个测试,甚至在 atomic() 块外部的 TestCase 中调用 select_for_update() 将(可能意外地)通过而不提高 TransactionManagementError。要正确测试 select_for_update(),您应该使用 TransactionTestCase

raw()

raw(raw_query, params=None, translations=None)

执行原始SQL查询,执行它并返回 django.db.models.query.RawQuerySet 实例。这个 RawQuerySet 实例可以像正常的 QuerySet 一样迭代,以提供对象实例。

有关详细信息,请参阅 执行原始SQL查询

警告

raw() 总是触发新的查询,不考虑以前的过滤。因此,它通常应该从 Manager 或从新的 QuerySet 实例调用。

不返回 QuerySet 的方法

以下 QuerySet 方法评估 QuerySet 并返回某个 以外 a QuerySet

这些方法不使用缓存(请参阅 缓存和 QuerySet)。相反,他们每次调用数据库时都会查询数据库。

get()

get(**kwargs)

返回与给定查找参数匹配的对象,其格式应为 Field lookups 中描述的格式。

如果找到多个对象,get() 将引发 MultipleObjectsReturnedMultipleObjectsReturned 异常是模型类的一个属性。

如果找不到给定参数的对象,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() 的任何关键字参数 - except (可选的一个称为 defaults)将在 get() 调用中使用。如果找到一个对象,get_or_create() 返回该对象和 False 的元组。如果找到多个对象,get_or_create 将提高 MultipleObjectsReturned。如果一个对象是 not,则 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 (参见 uniqueunique_together)在数据库级别不强制实现唯一性,则此方法容易出现竞争条件,这可能导致同时插入具有相同参数的多个行。

如果您使用MySQL,请务必使用 READ COMMITTED 隔离级别而不是 REPEATABLE READ (默认),否则您可能会看到 get_or_create 将引发 IntegrityError 的情况,但该对象不会出现在随后的 get() 调用中。

最后,在Django视图中使用 get_or_create()。请确保只在 POST 请求中使用它,除非你有很好的理由不要。 GET 请求不应对数据有任何影响。相反,如果对网页的请求对您的数据有副作用,请使用 POST。有关更多信息,请参阅HTTP规范中的 Safe methods

警告

您可以使用 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)

你可以通过Book的章节字段使用 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 是用于更新对象的(字段,值)对的字典。

返回 (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() 中所描述的,该方法易于出现竞争条件,如果在数据库级别不强制实现唯一性,则可能导致同时插入多行。

bulk_create()

bulk_create(objs, batch_size=None)

此方法以有效的方式将提供的对象列表插入数据库(通常只有1个查询,无论有多少个对象):

>>> Entry.objects.bulk_create([
...     Entry(headline='This is a test'),
...     Entry(headline='This is only a test'),
... ])

这有一些注意事项:

  • 将不会调用模型的 save() 方法,并且将不发送 pre_savepost_save 信号。

  • 它不适用于多表继承场景中的子模型。

  • 如果模型的主键是 AutoField,它不会像 save() 那样检索和设置主键属性,除非数据库后端支持它(当前为PostgreSQL)。

  • 它不适用于多对多关系。

Changed in Django 1.9:

增加了对使用 bulk_create() 和代理模型的支持。

Changed in Django 1.10:

支持在使用PostgreSQL时使用 bulk_create() 创建的对象上设置主键。

batch_size 参数控制在单个查询中创建多少个对象。默认是在一个批处理中创建所有对象,除了SQLite,其中默认值为每个查询最多使用999个变量。

count()

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(),而不是将所有记录加载到Python对象中,并在结果上调用 len() (除非您需要将对象加载到内存中,在这种情况下,len() 将会更快)。

根据您使用的数据库(例如PostgreSQL vs. MySQL),count() 可能返回一个长整型而不是普通的Python整数。这是一个潜在的实现怪癖,不应该引起任何现实世界的问题。

注意,如果你想要一个 QuerySet 中的项目数量,并且还从中检索模型实例(例如,通过迭代它),使用 len(queryset) 可能更有效率,这不会导致像 count() 的额外的数据库查询。

in_bulk()

in_bulk(id_list=None)

获取主键值的列表,并返回将每个主键值映射到具有给定ID的对象实例的字典。如果未提供列表,则返回查询集中的所有对象。

例:

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

如果你通过 in_bulk() 一个空列表,你会得到一个空的字典。

Changed in Django 1.10:

在旧版本中,id_list 是必需的参数。

iterator()

iterator()

评估 QuerySet (通过执行查询),并在结果上返回一个迭代器(参见 PEP 234)。 QuerySet 通常在内部缓存其结果,以便重复的评估不会导致额外的查询。相反,iterator() 将直接读取结果,而不在 QuerySet 级别进行任何缓存(内部,默认迭代器调用 iterator() 并缓存返回值)。对于返回大量对象的 QuerySet,您只需要访问一次,这可以导致更好的性能和显着减少内存。

请注意,在已经评估的 QuerySet 上使用 iterator() 将会强制它再次评估,重复查询。

此外,使用 iterator() 导致先前的 prefetch_related() 调用被忽略,因为这两个优化一起没有意义。

警告

一些Python数据库驱动程序(如 psycopg2)使用客户端游标(使用 connection.cursor() 实例化和Django的ORM使用)来执行缓存。使用 iterator() 不会影响数据库驱动程序级别的缓存。要禁用此缓存,请查看 server side cursors

latest()

latest(field_name=None)

使用作为日期字段提供的 field_name,按日期返回表中的最新对象。

此示例根据 pub_date 字段返回表中的最新 Entry:

Entry.objects.latest('pub_date')

如果您的模型的 指定 get_latest_by,您可以离开 field_name 参数 earliest()latest()。默认情况下,Django将使用 get_latest_by 中指定的字段。

get() 一样,如果没有具有给定参数的对象,earliest()latest() 会产生 DoesNotExist

注意,earliest()latest() 的存在纯粹是为了方便和可读性。

earliest()latest() 可以返回具有null日期的实例。

由于将排序委派给数据库,因此如果使用不同的数据库,则允许空值的字段的结果可能会有不同的排序。例如,PostgreSQL和MySQL排序空值,如同它们高于非空值,而SQLite则相反。

您可能想要过滤掉空值:

Entry.objects.filter(pub_date__isnull=False).latest('pub_date')

earliest()

earliest(field_name=None)

除了方向改变之外,工作原理类似于 latest()

first()

first()

返回由查询集匹配的第一个对象,如果没有匹配的对象,则返回 None。如果 QuerySet 没有定义顺序,则查询集自动由主键排序。

例:

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 中描述。由于聚合也是 查询表达式,您可以将聚合与其他聚合或值组合以创建复杂聚合。

使用关键字参数指定的聚合将使用关键字作为注释的名称。匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成名称。复杂聚合不能使用匿名参数,并且必须将关键字参数指定为别名。

例如,当您使用博客条目时,您可能想知道贡献博客条目的作者数量:

>>> 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。这试图以最简单和最快的方式尽可能地执行查询,但是 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_datecomments_on。)

您可以更新多个字段 - 没有限制多少。例如,在这里我们更新 comments_onheadline 字段:

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

您不能在 QuerySet 上调用 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_savepost_save 信号(这是调用 Model.save() 的结果)。如果你想为一个有自定义 save() 方法的模型更新一堆记录,循环遍历它们并调用 save(),像这样:

for e in Entry.objects.filter(pub_date__year=2010):
    e.comments_on = False
    e.save()

delete()

delete()

QuerySet 中的所有行执行SQL删除查询,并返回删除的对象数以及每个对象类型删除数量的字典。

delete() 即时应用。您不能在 QuerySet 上调用 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})
Changed in Django 1.9:

添加了描述删除对象数的返回值。

默认情况下,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})

此级联行为可通过 ForeignKeyon_delete 参数进行自定义。

delete() 方法执行批量删除,不会在模型上调用任何 delete() 方法。然而,它会为所有删除的对象(包括级联删除)发出 pre_deletepost_delete 信号。

Django需要将对象提取到内存中以发送信号和处理级联。然而,如果没有级联和没有信号,那么Django可以采取快速路径并删除对象而不提取到内存中。对于大型删除,这可以显着减少内存使用。执行的查询量也可以减少。

设置为 on_delete DO_NOTHING 的外键不会阻止删除快速路径。

注意,在对象删除中生成的查询是可以改变的实现细节。

as_manager()

classmethod as_manager()

类方法,返回 Manager 的实例与 QuerySet 的方法的副本。有关详细信息,请参阅 使用 QuerySet 方法创建管理器

Field 查找

字段查找是如何指定SQL WHERE 子句的内容。它们被指定为 QuerySet 方法 filter()exclude()get() 的关键字参数。

有关介绍,请参阅 模型和数据库查询文档

Django的内置查找列在下面。也可以为模型字段写入 自定义查找

为了方便当没有提供查找类型时(如在 Entry.objects.get(id=14) 中),假定查找类型是 exact

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;

MySQL比较

在MySQL中,数据库表的“排序规则”设置确定 exact 比较是否区分大小写。这是一个数据库设置,not 一个Django设置。可以将MySQL表配置为使用区分大小写的比较,但涉及一些折衷。有关更多信息,请参阅 数据库 文档中的 核对部分

iexact

不区分大小写的完全匹配。如果为比较提供的值是 None,它将被解释为一个SQL NULL (更多细节参见 isnull)。

例:

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;

注意第一个查询将匹配 'Beatles Blog''beatles blog''BeAtLes BLoG' 等。

SQLite用户

当使用SQLite后端和Unicode(非ASCII)字符串时,请记住 数据库笔记 关于字符串比较。 SQLite不对Unicode字符串进行不区分大小写的匹配。

contains

区分敏感遏制试验。

例:

Entry.objects.get(headline__contains='Lennon')

SQL等效:

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

注意这将匹配标题 'Lennon honored today',但不是 'lennon honored today'

SQLite用户

SQLite不支持区分大小写的 LIKE 语句; containsicontains 一样用于SQLite。有关详细信息,请参阅 数据库笔记

icontains

不区分大小写的遏制测试。

例:

Entry.objects.get(headline__icontains='Lennon')

SQL等效:

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

SQLite用户

当使用SQLite后端和Unicode(非ASCII)字符串时,请记住 数据库笔记 关于字符串比较。

in

在给定的列表中。

例:

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

SQL等效:

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

您还可以使用查询集动态评估值列表,而不是提供文字值列表:

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

此查询集将作为subselect语句求值:

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

如果将由 values()values_list() 产生的 QuerySet 作为 __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)

性能注意事项

对于使用嵌套查询和了解数据库服务器的性能特征(如果有疑问,基准!)要谨慎。一些数据库后端,最着名的是MySQL,不能很好地优化嵌套查询。在这些情况下,提取值列表然后将其传递到第二个查询是更有效的。也就是说,执行两个查询而不是一个:

values = Blog.objects.filter(
        name__contains='Cheddar').values_list('pk', flat=True)
entries = Entry.objects.filter(blog__in=list(values))

注意 list() 调用博客 QuerySet 强制执行第一个查询。没有它,将执行嵌套查询,因为 QuerySet 是懒惰的

gt

比...更棒。

例:

Entry.objects.filter(id__gt=4)

SQL等效:

SELECT ... WHERE id > 4;

gte

大于或等于。

lt

少于。

lte

小于或等于。

startswith

区分大小写启动。

例:

Entry.objects.filter(headline__startswith='Will')

SQL等效:

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

SQLite不支持区分大小写的 LIKE 语句; startswithistartswith 一样用于SQLite。

istartswith

不区分大小写。

例:

Entry.objects.filter(headline__istartswith='will')

SQL等效:

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

SQLite用户

当使用SQLite后端和Unicode(非ASCII)字符串时,请记住 数据库笔记 关于字符串比较。

endswith

区分大小写。

例:

Entry.objects.filter(headline__endswith='cats')

SQL等效:

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

SQLite用户

SQLite不支持区分大小写的 LIKE 语句; endswithiendswith 一样用于SQLite。有关更多信息,请参阅 数据库笔记 文档。

iendswith

不区分大小写。

例:

Entry.objects.filter(headline__iendswith='will')

SQL等效:

SELECT ... WHERE headline ILIKE '%will'

SQLite用户

当使用SQLite后端和Unicode(非ASCII)字符串时,请记住 数据库笔记 关于字符串比较。

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

您可以在任何可以在SQL中使用 BETWEENrange 中使用 - 用于日期,数字和偶数个字符。

警告

过滤具有日期的 DateTimeField 将不包括最后一天的项目,因为界限被解释为“给定日期上的0am”。如果 pub_dateDateTimeField,上面的表达式将变成这个SQL:

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

一般来说,你不能混合日期和数据时间。

date

New in Django 1.9.

对于datetime字段,将值转换为日期。允许链接额外的字段查找。获取日期值。

例:

Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

(此查找不包括等效的SQL代码片段,因为相关查询的实现因不同数据库引擎而异)。

USE_TZTrue 时,字段在过滤之前转换为当前时区。

year

对于日期和日期时间字段,精确的年匹配。允许链接额外的字段查找。整数年。

例:

Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)

SQL等效:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
SELECT ... WHERE pub_date >= '2005-01-01';

(确切的SQL语法因每个数据库引擎而异)。

USE_TZTrue 时,datetime字段在过滤之前转换为当前时区。

Changed in Django 1.9:

允许链接附加字段查找。

month

对于日期和日期时间字段,确切的月匹配。允许链接额外的字段查找。取整数1(1月)至12(12月)。

例:

Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)

SQL等效:

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

(确切的SQL语法因每个数据库引擎而异)。

USE_TZTrue 时,datetime字段在过滤之前转换为当前时区。这需要 数据库中的时区定义

Changed in Django 1.9:

允许链接附加字段查找。

day

对于日期和日期时间字段,精确的日匹配。允许链接额外的字段查找。采用整数日。

例:

Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)

SQL等效:

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

(确切的SQL语法因每个数据库引擎而异)。

请注意,这将匹配每月第三天有pub_date的任何记录,例如1月3日,7月3日等。

USE_TZTrue 时,datetime字段在过滤之前转换为当前时区。这需要 数据库中的时区定义

Changed in Django 1.9:

允许链接附加字段查找。

week_day

对于日期和日期时间字段,“星期几”匹配。允许链接额外的字段查找。

取整数值,表示星期几从1(星期日)到7(星期六)。

例:

Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)

(此查找不包括等效的SQL代码片段,因为相关查询的实现因不同数据库引擎而异)。

请注意,这将匹配 pub_date 落在星期一(一周的第2天)的任何记录,而不管其发生的月份或年份。周日被索引,第1天为星期天,第7天为星期六。

USE_TZTrue 时,datetime字段在过滤之前转换为当前时区。这需要 数据库中的时区定义

Changed in Django 1.9:

允许链接附加字段查找。

hour

对于日期时间和时间字段,精确的小时匹配。允许链接额外的字段查找。取0和23之间的整数。

例:

Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)

SQL等效:

SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';

(确切的SQL语法因每个数据库引擎而异)。

对于datetime字段,当 USE_TZTrue 时,在过滤之前将值转换为当前时区。

Changed in Django 1.9:

在SQLite上添加了对 TimeField 的支持(其他数据库支持它为1.7)。

Changed in Django 1.9:

允许链接附加字段查找。

minute

对于日期时间和时间字段,精确的分钟匹配。允许链接额外的字段查找。取0和59之间的整数。

例:

Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)

SQL等效:

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

(确切的SQL语法因每个数据库引擎而异)。

对于日期时间字段,当 USE_TZTrue 时,在过滤之前将值转换为当前时区。

Changed in Django 1.9:

在SQLite上添加了对 TimeField 的支持(其他数据库支持它为1.7)。

Changed in Django 1.9:

允许链接附加字段查找。

second

对于日期时间和时间字段,精确的第二个匹配。允许链接额外的字段查找。取0和59之间的整数。

例:

Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)

SQL等效:

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

(确切的SQL语法因每个数据库引擎而异)。

对于datetime字段,当 USE_TZTrue 时,在过滤之前将值转换为当前时区。

Changed in Django 1.9:

在SQLite上添加了对 TimeField 的支持(其他数据库支持它为1.7)。

Changed in Django 1.9:

允许链接附加字段查找。

isnull

采用 TrueFalse,它们分别对应于 IS NULLIS NOT NULL 的SQL查询。

例:

Entry.objects.filter(pub_date__isnull=True)

SQL等效:

SELECT ... WHERE pub_date IS NULL;

regex

区分大小写的正则表达式匹配。

正则表达式语法是正在使用的数据库后端的语法。在SQLite没有内置正则表达式支持的情况下,此功能由(Python)用户定义的REGEXP函数提供,因此正则表达式语法是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

不区分大小写的正则表达式匹配。

例:

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

SQL等价物:

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 模块中提供了以下聚合函数。有关如何使用这些聚合函数的详细信息,请参阅 聚合的主题指南。请参阅 Aggregate 文档以了解如何创建聚合。

警告

SQLite无法处理开箱即用的日期/时间字段的聚合。这是因为在SQLite中没有本机日期/时间字段,Django当前使用文本字段来模拟这些功能。尝试在SQLite中的日期/时间字段上使用聚合将会引发 NotImplementedError

注意

当与空 QuerySet 一起使用时,聚合函数返回 None。例如,如果 QuerySet 不包含任何条目,则 Sum 聚合函数返回 None 而不是 0。一个例外是 Count,如果 QuerySet 为空,它返回 0

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

expression

引用模型上字段的字符串,或 查询表达式

output_field

一个可选参数,表示返回值的 模型领域

注解

当组合多个字段类型时,Django只能确定 output_field,如果所有字段都是相同类型。否则,您必须自己提供 output_field

**extra

可为聚合生成的SQL提供额外上下文的关键字参数。

Avg

class Avg(expression, output_field=FloatField(), **extra)[源代码]

返回给定表达式的平均值,除非指定了不同的 output_field,否则它必须为数字。

  • 默认别名:<field>__avg

  • 返回类型:float (或指定 output_field 的类型)

Changed in Django 1.9:

添加了 output_field 参数以允许聚合非数字列,例如 DurationField

Count

class Count(expression, distinct=False, **extra)[源代码]

返回通过所提供的表达式相关的对象数。

  • 默认别名:<field>__count

  • 返回类型:int

有一个可选参数:

distinct

如果 distinct=True,计数将只包括唯一实例。这是与 COUNT(DISTINCT <field>) 等效的SQL。默认值为 False

Max

class Max(expression, output_field=None, **extra)[源代码]

返回给定表达式的最大值。

  • 默认别名:<field>__max

  • 返回类型:与输入字段相同,或 output_field (如果提供)

Min

class Min(expression, output_field=None, **extra)[源代码]

返回给定表达式的最小值。

  • 默认别名:<field>__min

  • 返回类型:与输入字段相同,或 output_field (如果提供)

StdDev

class StdDev(expression, sample=False, **extra)[源代码]

返回所提供表达式中数据的标准偏差。

  • 默认别名:<field>__stddev

  • 返回类型:float

有一个可选参数:

sample

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

SQLite

SQLite不提供开箱即用的 StdDev。一个实现可用作SQLite的扩展模块。有关获取和安装此扩展的说明,请咨询 SQlite documentation

Sum

class Sum(expression, output_field=None, **extra)[源代码]

计算给定表达式的所有值的总和。

  • 默认别名:<field>__sum

  • 返回类型:与输入字段相同,或 output_field (如果提供)

Variance

class Variance(expression, sample=False, **extra)[源代码]

返回所提供表达式中数据的方差。

  • 默认别名:<field>__variance

  • 返回类型:float

有一个可选参数:

sample

默认情况下,Variance 返回总体方差。但是,如果 sample=True,返回值将是样本方差。

SQLite

SQLite不提供开箱即用的 Variance。一个实现可用作SQLite的扩展模块。有关获取和安装此扩展的说明,请咨询 SQlite documentation