Skip to main content

进行查询

一旦你创建了你的 数据模型,Django自动给你一个数据库抽象API,让你创建,检索,更新和删除对象。本文档说明如何使用此API。有关所有各种模型查找选项的完整详细信息,请参阅 数据模型参考

在本指南(以及参考文献)中,我们将参考以下模型,其中包括Weblog应用程序:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):              # __unicode__ on Python 2
        return self.headline

创建对象

为了在Python对象中表示数据库表数据,Django使用一个直观的系统:模型类表示数据库表,该类的实例表示数据库表中的特定记录。

要创建一个对象,使用模型类的关键字参数实例化它,然后调用 save() 将其保存到数据库。

假设模型存在于 mysite/blog/models.py 文件中,这里是一个例子:

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

这在后台执行 INSERT SQL语句。 Django不会打到数据库,直到你显式调用 save()

save() 方法没有返回值。

参见

save() 采用了一些这里没有描述的高级选项。有关完整的详细信息,请参阅 save() 的文档。

要在单个步骤中创建和保存对象,请使用 create() 方法。

保存对象的更改

要保存对已经在数据库中的对象的更改,请使用 save()

给定已经保存到数据库的 Blog 实例 b5,此示例更改其名称并更新其在数据库中的记录:

>>> b5.name = 'New name'
>>> b5.save()

这在后台执行 UPDATE SQL语句。 Django不会打到数据库,直到你显式调用 save()

保存 ForeignKeyManyToManyField 字段

更新 ForeignKey 字段的方式与保存正常字段完全相同 - 只需将正确类型的对象分配给相关字段。此示例更新 Entry 实例 entryblog 属性,假设 EntryBlog 的适当实例已保存到数据库(因此我们可以在下面检索它们):

>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

更新 ManyToManyField 的工作方式略有不同 - 使用字段上的 add() 方法向关系中添加记录。此示例将 Author 实例 joe 添加到 entry 对象:

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

要一次将多个记录添加到 ManyToManyField,请在调用 add() 时包含多个参数,如下所示:

>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

Django会抱怨如果你试图分配或添加一个错误类型的对象。

检索对象

要从数据库中检索对象,请通过模型类上的 Manager 构建 QuerySet

QuerySet 表示数据库中的对象集合。它可以有零个,一个或多个 filters。过滤器根据给定的参数缩小查询结果。在SQL术语中,QuerySet 等同于 SELECT 语句,并且过滤器是限制条款,例如 WHERELIMIT

你通过使用你的模型的 Manager 得到 QuerySet。每个模型至少有一个 Manager,它默认称为 objects。直接通过模型类访问它,像这样:

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
    ...
AttributeError: "Manager isn't accessible via Blog instances."

注解

Managers 只能通过模型类而不是模型实例访问,以强制“表级”操作和“记录级”操作之间的分离。

Manager 是模型的 QuerySets 的主要来源。例如,Blog.objects.all() 返回包含数据库中所有 Blog 对象的 QuerySet

检索所有对象

从表中检索对象的最简单的方法是获取所有对象。为此,在 Manager 上使用 all() 方法:

>>> all_entries = Entry.objects.all()

all() 方法返回数据库中所有对象的 QuerySet

使用过滤器检索特定对象

all() 返回的 QuerySet 描述数据库表中的所有对象。通常,你需要只选择一组完整的对象的子集。

要创建此类子集,请细化初始 QuerySet,添加过滤条件。改进 QuerySet 的两种最常见的方法是:

filter(**kwargs)

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

exclude(**kwargs)

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

查找参数(上述函数定义中的 **kwargs)应采用下面 Field lookups 中描述的格式。

例如,要从2006年获得 QuerySet 的博客条目,请使用 filter():

Entry.objects.filter(pub_date__year=2006)

使用默认管理器类,它是相同的:

Entry.objects.all().filter(pub_date__year=2006)

链接过滤器

改进 QuerySet 的结果本身是一个 QuerySet,所以可以链接改进在一起。例如:

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )

这需要数据库中所有条目的初始 QuerySet,添加一个过滤器,然后是排除,然后是另一个过滤器。最终结果是 QuerySet,其中包含所有条目,标题以“What”开头,在2005年1月30日之前发布,并且当前日期。

过滤的 QuerySet 是唯一的

每次改进 QuerySet 时,您都会得到一个全新的 QuerySet,而不是以前的 QuerySet。每个细化创建一个单独的和独特的 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())

这三个 QuerySets 是分开的。第一个是基本 QuerySet,其中包含所有包含以“What”开头的标题的条目。第二个是第一个的子集,具有排除其 pub_date 是今天或将来的记录的附加标准。第三个是第一个的子集,具有仅选择 pub_date 是今天或将来的记录的附加标准。初始 QuerySetq1)不受精化过程的影响。

QuerySet 是懒惰的

QuerySets 是懒惰的 - 创建 QuerySet 的行为不涉及任何数据库活动。你可以将过滤器整个堆栈在一起,Django实际上不会运行查询,直到 QuerySetevaluated。看看这个例子:

>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)

虽然这看起来像三个数据库命中,实际上它只在数据库命中一次,在最后一行(print(q))。一般来说,QuerySet 的结果不会从数据库中提取,直到您“询问”它们。当你这样做,QuerySetevaluated 通过访问数据库。有关确切何时进行评估的详细信息,请参阅 当评估 QuerySet 时

使用 get() 检索单个对象

filter() 将总是给你一个 QuerySet,即使只有一个对象匹配查询 - 在这种情况下,它将是一个 QuerySet 包含单个元素。

如果您知道只有一个对象与您的查询匹配,您可以在 Manager 上使用 get() 方法,该方法直接返回对象:

>>> one_entry = Entry.objects.get(pk=1)

您可以使用任何查询表达式与 get(),就像 filter() - 再次,请参阅下面的 Field lookups

注意,在使用 get() 和使用具有 [0] 切片的 filter() 之间存在差异。如果没有匹配查询的结果,get() 将引发 DoesNotExist 异常。此异常是执行查询的模型类的属性 - 因此在上面的代码中,如果没有主键为1的 Entry 对象,Django将提升 Entry.DoesNotExist

类似地,如果多个项目与 get() 查询匹配,Django将投诉。在这种情况下,它将提高 MultipleObjectsReturned,这也是模型类本身的一个属性。

其他 QuerySet 方法

大多数时候,当您需要从数据库中查找对象时,您将使用 all()get()filter()exclude()。然而,这远不是所有的;请参阅 QuerySet API参考 获取所有各种 QuerySet 方法的完整列表。

限制 QuerySet

使用Python的数组切片语法的一个子集来限制您的 QuerySet 到一定数量的结果。这相当于SQL的 LIMITOFFSET 子句。

例如,这返回前5个对象(LIMIT 5):

>>> Entry.objects.all()[:5]

这返回第六到第十个对象(OFFSET 5 LIMIT 5):

>>> Entry.objects.all()[5:10]

不支持负索引(即 Entry.objects.all()[-1])。

通常,切片 QuerySet 返回一个新的 QuerySet - 它不评估查询。一个例外是如果你使用Python的slice语法的“step”参数。例如,这将实际执行查询,以返回前10的每个 second 对象的列表:

>>> Entry.objects.all()[:10:2]

要检索 single 对象而不是列表(例如 SELECT foo FROM bar LIMIT 1),请使用简单的索引而不是切片。例如,在按标题字母顺序排序后,这将返回数据库中的第一个 Entry:

>>> Entry.objects.order_by('headline')[0]

这大致相当于:

>>> Entry.objects.order_by('headline')[0:1].get()

注意,但是,第一个将提高 IndexError,而第二个将提高 DoesNotExist,如果没有对象匹配给定的标准。有关详细信息,请参阅 get()

字段查找

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

基本查找关键字参数采用 field__lookuptype=value 的形式。 (这是一个双下划线)。例如:

>>> Entry.objects.filter(pub_date__lte='2006-01-01')

翻译(大致)到以下SQL:

SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

这是怎么可能的

Python能够定义接受任意名称和值参数的函数,这些参数的名称和值在运行时被求值。有关更多信息,请参阅官方Python教程中的 Keyword Arguments

在查找中指定的字段必须是模型字段的名称。有一个例外,但是在 ForeignKey 的情况下,您可以指定字段名称后缀为 _id。在这种情况下,value参数应包含外部模型主键的原始值。例如:

>>> Entry.objects.filter(blog_id=4)

如果传递一个无效的关键字参数,查找函数将引发 TypeError

数据库API支持大约二十种查找类型;一个完整的参考可以在 字段查找引用 中找到。为了让您了解可用的功能,以下是您可能会使用的一些常见查找:

exact

“完全匹配”。例如:

>>> Entry.objects.get(headline__exact="Cat bites dog")

将按照以下行生成SQL:

SELECT ... WHERE headline = 'Cat bites dog';

如果不提供查找类型 - 即,如果关键字参数不包含双下划线,则查找类型假定为 exact

例如,以下两个语句是等效的:

>>> Blog.objects.get(id__exact=14)  # Explicit form
>>> Blog.objects.get(id=14)         # __exact is implied

这是为了方便,因为 exact 查找是常见的情况。

iexact

不区分大小写的匹配。所以,查询:

>>> Blog.objects.get(name__iexact="beatles blog")

将匹配 Blog 标题为 "Beatles Blog""beatles blog",甚至 "BeAtlES blOG"

contains

区分敏感遏制试验。例如:

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

大致翻译成这个SQL:

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

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

还有一个不区分大小写的版本,icontains

startswithendswith

开始和结束 - 分别与搜索。还有称为 istartswithiendswith 的不区分大小写的版本。

同样,这只是划伤表面。完整的参考可以在 字段查找引用 中找到。

查找跨关系

Django提供了一种强大而直观的方式来“跟踪”查找中的关系,在后台自动处理SQL JOIN s。要跨越关系,只需使用模型之间的相关字段的字段名称(用双下划线分隔),直到找到所需的字段。

此示例检索具有其 name'Beatles Blog'Blog 的所有 Entry 对象:

>>> Entry.objects.filter(blog__name='Beatles Blog')

这个跨度可以像你想要的一样深。

它也向后工作。要引用“反向”关系,只需使用模型的小写名称。

此示例检索具有至少一个其 headline 包含 'Lennon'Entry 的所有 Blog 对象:

>>> Blog.objects.filter(entry__headline__contains='Lennon')

如果您跨多个关系进行过滤,并且其中一个中间模型没有满足过滤条件的值,Django会将其视为空(所有值都是 NULL),但有效,对象。所有这些意味着不会产生错误。例如,在此过滤器中:

Blog.objects.filter(entry__authors__name='Lennon')

(如果存在相关的 Author 模型),如果没有与条目相关联的 author,则将其视为没有附加 name,而不是由于缺少 author 而引起错误。通常这正是你想要发生的。唯一可能会混淆的情况是,如果你使用 isnull。从而:

Blog.objects.filter(entry__authors__name__isnull=True)

将返回在 author 上具有空 nameBlog 对象,以及在 entry 上具有空 author 的那些对象。如果你不想要那些后面的对象,你可以写:

Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)

跨越多值关系

当您基于 ManyToManyField 或反向 ForeignKey 过滤对象时,有两种不同类型的过滤器,您可能会感兴趣。考虑 Blog/Entry 关系(BlogEntry 是一对多关系)。我们可能有兴趣寻找博客,其中有一个条目在标题中有 “Lennon”,并在2008年发布。或者我们可能想找到在标题中有 “Lennon” 条目的博客,以及在2008年发布的条目由于存在与单个 Blog 相关联的多个条目,所以这些查询都是可能的并且在某些情况下是有意义的。

ManyToManyField 也会出现同样的情况。例如,如果 Entry 具有称为 tagsManyToManyField,我们可能想要找到链接到标签 “music”“bands” 的条目,或者我们可能想要一个包含标签的条目,其名称为 “music”,状态为 “public”

为了处理这两种情况,Django有一个一致的处理 filter() 调用的方法。同时应用单个 filter() 调用中的所有内容以过滤掉符合所有这些要求的项目。连续 filter() 调用进一步限制对象集合,但对于多值关系,它们适用于链接到主模型的任何对象,而不一定是早期 filter() 调用所选择的那些对象。

这听起来有点混乱,所以希望一个例子会澄清。要选择包含标题中包含 “Lennon” 并且在2008年发布的条目(同一条目满足两个条件)的所有博客,我们将写入:

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)

要在标题 以及 中选择包含 “Lennon” 条目的所有博客,在2008年发布的条目,我们将写入:

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

假设只有一个博客有两个条目包含 “Lennon” 和2008年的条目,但2008年的条目都没有包含 “Lennon”。第一个查询不会返回任何博客,但第二个查询将返回那个博客。

在第二示例中,第一过滤器将查询限制到链接到具有标题中的 “Lennon” 的条目的所有博客。第二过滤器将博客 further 集合限制为也被链接到在2008年发布的条目的博客。由第二过滤器选择的条目可以与第一过滤器中的条目相同或不同。我们正在用每个过滤器语句过滤 Blog 项目,而不是 Entry 项目。

注解

对于跨越多值关系的查询,如上所述,filter() 的行为不等效地实现为 exclude()。相反,单个 exclude() 调用中的条件不一定引用相同的项目。

例如,以下查询将排除在2008年发布的标题 and 条目中包含具有 “Lennon”both 条目的博客:

Blog.objects.exclude(
    entry__headline__contains='Lennon',
    entry__pub_date__year=2008,
)

但是,与使用 filter() 时的行为不同,这不会限制基于满足这两个条件的条目的博客。为了做到这一点,即选择不包含在2008年发布的 “Lennon” 发布的条目的所有博客,您需要进行两个查询:

Blog.objects.exclude(
    entry__in=Entry.objects.filter(
        headline__contains='Lennon',
        pub_date__year=2008,
    ),
)

过滤器可以引用模型上的字段

在迄今为止给出的例子中,我们构造了将模型字段的值与常数进行比较的过滤器。但是,如果你想比较模型字段的值与同一模型上的另一个字段怎么办?

Django提供 F expressions 允许这样的比较。 F() 的实例充当对查询中的模型字段的引用。然后,可以在查询过滤器中使用这些引用来比较同一模型实例上的两个不同字段的值。

例如,要找到所有博客条目的列表,其中有比pingback更多的评论,我们构造一个 F() 对象来引用pingback计数,并在查询中使用该 F() 对象:

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

Django支持对 F() 对象使用加法,减法,乘法,除法,模数和幂运算,这两个对象都具有常量和其他 F() 对象。为了找到所有的博客条目具有多于 twice 的许多评论作为pingbacks,我们修改查询:

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

要查找条目的评分小于pingback计数和注释计数的总和的所有条目,我们将发出查询:

>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

您还可以使用双下划线符号来跨越 F() 对象中的关系。具有双下划线的 F() 对象将引入访问相关对象所需的任何连接。例如,要检索作者的名称与博客名称相同的所有条目,我们可以发出查询:

>>> Entry.objects.filter(authors__name=F('blog__name'))

对于日期和日期/时间字段,可以添加或减去 timedelta 对象。以下内容将返回在发布之后超过3天进行了修改的所有条目:

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

F() 对象支持例如 .bitand().bitor() 的按位操作:

>>> F('somefield').bitand(16)

pk 查找快捷方式

为了方便起见,Django提供了一个 pk 查找快捷方式,代表“主键”。

在示例 Blog 模型中,主键是 id 字段,因此这三个语句是等效的:

>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact

pk 的使用不限于 __exact 查询 - 任何查询项都可以与 pk 组合以对模型的主键执行查询:

# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])

# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)

pk 查找也在连接之间工作。例如,这三个语句是等效的:

>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3)        # __exact is implied
>>> Entry.objects.filter(blog__pk=3)        # __pk implies __id__exact

LIKE 声明中转义百分号和下划线

字段查找等同于 LIKE SQL语句(iexactcontainsicontainsstartswithistartswithendswithiendswith)将自动转义 LIKE 语句中使用的两个特殊字符 - 百分号和下划线。 (在 LIKE 语句中,百分号表示多字符通配符,下划线表示单字符通配符。)

这意味着事情应该直观,所以抽象不泄漏。例如,要检索包含百分号的所有条目,只需使用百分号作为任何其他字符:

>>> Entry.objects.filter(headline__contains='%')

Django照顾你的报价;结果SQL将看起来像这样:

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

下划线也一样。百分号和下划线都是透明地处理的。

缓存和 QuerySet

每个 QuerySet 包含一个缓存以最小化数据库访问。了解其工作原理将允许您编写最有效的代码。

在新创建的 QuerySet 中,高速缓存为空。第一次评估 QuerySet,因此,发生数据库查询 - Django将查询结果保存在 QuerySet 的缓存中,并返回已明确请求的结果(例如,如果正在重复 QuerySet,则返回下一个元素过度)。随后对 QuerySet 的评估重用缓存的结果。

保持这种缓存行为,因为它可能会咬你,如果你不正确使用您的 QuerySet 。例如,以下将创建两个 QuerySet s,评估它们,并将它们丢弃:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

这意味着相同的数据库查询将执行两次,有效地将您的数据库负载加倍。另外,两个列表可能不包括相同的数据库记录,因为 Entry 可能已经在两个请求之间的分割秒中被添加或删除。

为了避免这个问题,只需保存 QuerySet 并重用它:

>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

QuerySet 未缓存时

查询集不总是缓存其结果。当仅评估查询集的 part 时,将检查缓存,但如果未填充,则不缓存由后续查询返回的项。具体来说,这意味着使用数组切片或索引的 限制查询集 将不会填充缓存。

例如,在查询集对象中重复获取某个索引将每次查询数据库:

>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again

但是,如果整个查询集已经被评估,则将检查缓存:

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache

下面是将导致整个查询集被计算并因此填充高速缓存的其他动作的一些示例:

>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)

注解

只打印查询集将不会填充缓存。这是因为对 __repr__() 的调用仅返回整个查询集的切片。

使用 Q 对象进行复杂查找

关键字参数查询 - 在 filter() 等中 - 被“AND”在一起。如果需要执行更复杂的查询(例如,使用 OR 语句的查询),则可以使用 Q objects

Q objectdjango.db.models.Q)是用于封装关键字参数集合的对象。这些关键字参数在上面的“字段查找”中指定。

例如,此 Q 对象封装单个 LIKE 查询:

from django.db.models import Q
Q(question__startswith='What')

Q 对象可以使用 &| 运算符组合。当在两个 Q 对象上使用运算符时,它产生一个新的 Q 对象。

例如,此语句生成表示两个 "question__startswith" 查询的“OR”的单个 Q 对象:

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

这等同于以下SQL WHERE 子句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

您可以通过将 Q 对象与 &| 运算符组合并使用括号分组来构成任意复杂度的语句。此外,Q 对象可以使用 ~ 运算符取反,允许组合查找结合正常查询和否定(NOT)查询:

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

获取关键字参数(例如 filter()exclude()get())的每个查找函数也可以作为位置(未命名)参数传递一个或多个 Q 对象。如果向查找函数提供多个 Q 对象参数,那么这些参数将被“AND”在一起。例如:

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

...大致翻译成SQL:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

查找函数可以混合使用 Q 对象和关键字参数。提供给查找函数(无论是关键字参数还是 Q 对象)的所有参数都被“AND”在一起。但是,如果提供了 Q 对象,它必须在任何关键字参数的定义之前。例如:

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

...将是有效的查询,等同于前面的示例;但:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

...将无效。

参见

Django单元测试中的 OR lookups examples 显示了 Q 的一些可能的用途。

比较对象

要比较两个模型实例,只需使用标准的Python比较运算符,双等号:==。在幕后,它比较两个模型的主键值。

使用上面的 Entry 示例,以下两个语句是等效的:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id

如果模型的主键不叫 id,没有问题。比较将总是使用主键,无论它被调用。例如,如果模型的主键字段称为 name,则这两个语句是等效的:

>>> some_obj == other_obj
>>> some_obj.name == other_obj.name

删除对象

删除方法,方便地,命名为 delete()。此方法立即删除对象并返回删除的对象数量和每个对象类型具有删除数量的字典。例:

>>> e.delete()
(1, {'weblog.Entry': 1})
Changed in Django 1.9:

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

您也可以批量删除对象。每个 QuerySet 都有一个 delete() 方法,删除该 QuerySet 的所有成员。

例如,这将删除 pub_date 年份为2005的所有 Entry 对象:

>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})

请记住,这将尽可能纯粹在SQL中执行,因此单个对象实例的 delete() 方法不一定会在过程中被调用。如果您在模型类上提供了一个自定义 delete() 方法,并且想要确保它被调用,您将需要“手动”删除该模型的实例(例如,通过迭代 QuerySet 并单独调用每个对象上的 delete())而不是使用 QuerySet 的批量 delete() 方法。

Changed in Django 1.9:

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

当Django删除一个对象时,默认情况下它会模拟SQL约束 ON DELETE CASCADE 的行为 - 换句话说,任何具有指向要删除的对象的外键的对象都将被删除。例如:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

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

注意,delete() 是在 Manager 本身上不暴露的唯一 QuerySet 方法。这是一个安全机制,以防止您意外请求 Entry.objects.delete(),并删除 all 条目。如果 do 要删除所有对象,则必须显式请求一个完整的查询集:

Entry.objects.all().delete()

复制模型实例

虽然没有用于复制模型实例的内置方法,但是可以容易地创建具有复制的所有字段的值的新实例。在最简单的情况下,您可以将 pk 设置为 None。使用我们的博客示例:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

如果使用继承,事情变得更复杂。考虑 Blog 的子类:

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3

由于继承如何工作,你必须将 pkid 都设置为None:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

此过程不复制不是模型数据库表的一部分的关系。例如,Entry 具有到 AuthorManyToManyField。复制条目后,必须为新条目设置多对多关系:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors.set(old_authors)

对于 OneToOneField,您必须复制相关对象并将其分配给新对象的字段,以避免违反一对一唯一约束。例如,假设 entry 已经如上所述地复制:

detail = EntryDetail.objects.all()[0]
detail.pk = None
detail.entry = entry
detail.save()

一次更新多个对象

有时,您希望为 QuerySet 中的所有对象将字段设置为特定值。您可以使用 update() 方法执行此操作。例如:

# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

您只能使用此方法设置非关系字段和 ForeignKey 字段。要更新非关系字段,请提供新值作为常量。要更新 ForeignKey 字段,请将新值设置为要指向的新模型实例。例如:

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

# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)

update() 方法立即应用,并返回查询匹配的行数(如果某些行已具有新值,则可能不等于更新的行数)。对正在更新的 QuerySet 的唯一限制是它只能访问一个数据库表:模型的主表。您可以根据相关字段进行过滤,但只能更新模型主表中的列。例:

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

# Update all the headlines belonging to this Blog.
>>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')

请注意,update() 方法直接转换为SQL语句。它是用于直接更新的批量操作。它不会在您的模型上运行任何 save() 方法,或者发出 pre_savepost_save 信号(这是调用 save() 的结果),或者符合 auto_now 字段选项。如果要保存 QuerySet 中的每个项目,并确保在每个实例上调用 save() 方法,则不需要任何特殊函数来处理它。只是循环他们和调用 save():

for item in my_queryset:
    item.save()

调用更新还可以使用 F expressions 根据模型中另一个字段的值更新一个字段。这对于根据计数器的当前值递增计数器特别有用。例如,为博客中的每个条目递增pingback计数:

>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

但是,与过滤器和排除子句中的 F() 对象不同,在更新中使用 F() 对象时,不能引入连接 - 只能引用正在更新的模型的本地字段。如果尝试引入具有 F() 对象的联接,则将引发 FieldError:

# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))

回退到原始SQL

如果你发现自己需要编写一个对Django的数据库映射器来说太复杂的SQL查询来处理,你可以回头用手写SQL。 Django有几个选项来编写原始SQL查询;见 执行原始SQL查询

最后,重要的是要注意,Django数据库层只是数据库的一个接口。您可以通过其他工具,编程语言或数据库框架访问您的数据库;没有Django特定的数据库。