Django 文档

当前文档仅适用于 Django SVN 版本,与上个版本有显著不同。上个版本文档请查阅 Django 1.0

制作查询 (Making queries)

创建完数据模型data models) 之后, Django 自动为你提供一套数据库抽象层的API,利用它可以完成创建,提取,更新,删除对象的操作。本文介绍如何使用这些API。关于查询时用到的参数,请查阅 数据模型参考 (data model reference)

在整个文档以及引用的文档中中,我们都将以下面这个博客为例( Blog 是博客站点,Author 是博客作者,Entry 是博客文章,在下文中我们分别以博客,作者,博文相称):

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

    def __unicode__(self):
        return self.name

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

    def __unicode__(self):
        return self.name

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

    def __unicode__(self):
        return self.headline

创建对象 (Creating objects)

Django 用一种很直观的表达方式将 Python 对象和数据表对应起来:一个 model 类对应一张数据表,一个 model 实例对应表中的某一行记录。

以创建对象为例:只要将关键字参数传递给 model 类,然后调用 save() 保存到数据库即可。

正如你所期望的那样,你可以根据 model 类所在的路径将 model 类导入到当前文件中(之所以强调这一点,是因为上一版本的 django 在导入 model 这方面做得并不好。)

假设所有 model 都在文件 mysite/blog/models.py中, 那么在下面这个例子中:

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

这段代码就会在幕后执行一条 INSERT SQL 语句。除非你显式地调用 save() 方法,否则 Django 不会保存到数据库中。

save() 方法不返回任何值。

请参见

save() 方法自带一些高级参数,这里并没有提及。 详见 save() 的文档。

你可以使用 `create()` 方法一次完成新建并保存对象的操作。

保存对象修改后的信息 (Saving changes to objects)

对于已经存在于数据库中的对象,要保存修改后的信息,仍是使用 save() 方法。

假设现在有一个 Blog 类的实例 b5 ,它已经保存到数据库中,下面的例子将改变它的名称,然后更新到数据库中:

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

这段代码在幕后执行一条 UPDATE SQL 语句。除非你显式地调用 save() 方法,否则 Django 不会更新数据库。

保存 ForeignKeyManyToManyField 字段 (Saving ForeignKey and ManyToManyField fields)

更新 ForeignKey 字段和保存普通字段没什么差别;只是在给字段分配对象时要注意对象类型一定要正确:

>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

更新 ManyToManyField 就有些不同;要在字段上使用 add() 方法来添加关系:

>> joe = Author.objects.create(name="Joe")
>> entry.authors.add(joe)

如果你在分配或添加对象时,使用了类型错误的对象,Django就会报错。

检索对象 (Retrieving objects)

要检索数据库中的对象,就要为你的 model 类构造一个 查询集 (QuerySet)Manager

一个 QuerySet 就代表数据库中的一组数据。它可以有一个或很多个,也可以没有过滤器(filter)--根据给定的参数对数据集做进一步筛选。在 SQL 术语中, QuerySet 相当于 SELECT 语句,filter 相当于 WHERELIMIT 这样的限定从句。

你可以利用 model 的 Manager 来得到 QuerySet 。每个 model 都有至少一个 Manager,默认情况下它被命名为 objects 。你可以直接通过 model 类来访问 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 只能由 model 类访问,而不能通过 model 实例访问。这是因为前者是表级操作,而后者只是记录级操作。

Manager 是 model 中主要的 QuerySets 。 它相当于一个"根" QuerySet ,用来描述数据库中所有对象。例如,Blog.objects 是包含数据表中所有 Blog 对象的初始 QuerySet

检索所有对象 (Retrieving all objects)

从表中检索对象的最简单方式就是一次得到所有对象,只要调用 Managerall() 方法即可:

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

all() 方法返回一个表示数据表中所有对象的 QuerySet

( Entry.objects 也是一个 QuerySet,为什么我们不直接使用 Entry.objects 呢? 这是因为 Entry.objects 是特殊的根 QuerySet,它不能被求值;而 all() 方法则返回一个可以被求值的 QuerySet。)

使用过滤器检索特定的对象 (Retrieving specific objects with filters)

Manager 提供的 QuerySet 描述了数据表中的所有对象。但通常情况下,你需要的只是数据集中的某个子集。

你可以添加筛选条件来缩小初始 QuerySet 的范围,从而获得数据子集。筛选 QuerySet 最常用的两种方式是:

filter(**kwargs)
返回满足筛选条件的新 QuerySet
exclude(**kwargs)
返回不满足筛选条件的新 QuerySet

这些筛选条件 (就是上述函数定义中的 **kwargs ) 在下面的 字段筛选条件 (Field lookups) 中有详细的介绍

举个例子,用 QuerySet 检索2006年发布的所有博文 ,可以使用 filter() :

Entry.objects.filter(pub_date__year=2006)

不必添加 all() -- Entry.objects.all().filter(...)。要从根 QuerySet 得到所有对象,才适用 all() 方法。

链式过滤 (Chaining filters)

筛选后得到 QuerySet 仍是一个 QuerySet,所以可以构造过滤链。例如:

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

这段代码用初始 QuerySet 获得数据表中所有记录,然后依次添加 filter ,exclude ,filter 三个过滤。最终得到的结果是一个新 QuerySet,它包含了大标题以"What"开头,并从2005年1月1日到目前为止出版的所有记录。

过滤后的查询集是唯一的 (Filtered querysets unique)

每次筛选 QuerySet后,都返回一个新的 QuerySet。它与前一个 QuerySet完全不同。每次筛选都创建一个独立而不同的 QuerySet。它可以保存,可以使用而且可以多次使用的。

例如:

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

这三个 QuerySets 是不同的。 第一个 QuerySet 包含大标题以"What"开头的所有记录。第二个则是第一个的子集,用一个附加的条件排除了出版日期 pub_date 是今天的记录。 第三个也是第一个的子集,它只保留出版日期 pub_date 是今天的记录。 最初的 QuerySet (q1) 没有受到筛选的影响。

查询集是惰性的 (QuerySets are lazy)

QuerySets 是惰性的 -- 创建 QuerySet 的动作不涉及任何数据库操作。你可以一直添加过滤器,在这个过程中,Django 不会执行任何数据库查询,除非 QuerySet 被执行. 看看下面这个例子:

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

虽然上面的代码看上去象是三个数据库操作,但实际上只在最后一行 (print q) 执行了一次数据库操作,。一般情况下, QuerySet 不能从数据库中主动地获得数据,得被动地由你来请求。对 QuerySet 求值就意味着 Django 会访问数据库。想了解对查询集何时求值,请查看 何时对查询集求值 (When QuerySets are evaluated).

其他查询集方法 (Other querysets mothods)

大多数情况使用 all(), filter()exclude() 就足够了。 但也有一些不常用的;请查看 查询API参考 (QuerySet API Reference) 中完整的 QuerySet 方法列表。

限制查询集范围 (Limiting QuerySets)

可以用 python 的数组切片语法来限制你的 QuerySet 以得到一部分结果。它等价于SQL中的 LIMITOFFSET

例如,下面的这个例子返回前五个对象 (LIMIT 5):

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

这个例子返回第六到第十之间的对象 (OFFSET 5 LIMIT 5):

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

Django 不支持对查询集做负数索引 (例如 Entry.objects.all()[-1]) 。

一般来说,对 QuerySet 切片会返回新的 QuerySet -- 这个过程中不会对运行查询。不过也有例外,如果你在切片时使用了 "step" 参数,查询集就会被求值,就在数据库中运行查询。举个例子,使用下面这个这个查询集返回前十个对象中的偶数次对象,就会运行数据库查询:

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

要检索单独的对象,而非列表 (比如 SELECT foo FROM bar LIMIT 1),可以直接使用索引来代替切片。举个例子,下面这段代码将返回大标题排序后的第一条记录 Entry:

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

大约等价于:

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

要注意的是:如果找不到符合条件的对象,第一种方法会抛出 IndexError ,而第二种方法会抛出 DoesNotExist。 详看 get()

字段筛选条件 (Field lookups)

字段筛选条件就是 SQL 语句中的 WHERE 从句。就是 Django 中的 QuerySetfilter(), 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 允许函式接受任意多 name-value 形式的参数,并在运行时才确定name和value的值。详情请参阅官方Python教程中的 关键字参数(Keyword Arguments)

如果你传递了一个无效的关键字参数,会抛出 TypeError 导常。

数据库 API 支持24种查询类型;可以在 字段筛选参考(field lookup reference) 查看详细的列表。为了给您一个直观的认识,这里我们列出一些常用的查询类型:

exact

"exact" 匹配。例如:

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

会生成如下的 SQL 语句:

SELECT ... WHERE headline = 'Man 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")

会匹配标题是 "Beatles Blog", "beatles blog", 甚至 "BeAtlES blOG" 的 Blog

contains

大小写敏感的模糊匹配。 例如:

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

大体可以翻译为如下的 SQL:

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

要注意这段代码匹配大标题 'Today Lennon honored' ,而不能匹配 'today lennon honored'

它也有一个忽略大小写的版本,就是 icontains

startswith, endswith
分别匹配开头和结尾,同样也有忽略大小写的版本 istartswithiendswith

再强调一次,这仅仅是简短介绍。完整的参考请参见 字段筛选条件参考(field lookup reference)

跨关系查询 (Lookups that span relationships)

Django 提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。

这个例子检索所有关联 Blogname 值为 'Beatles Blog' 的所有 Entry 对象:

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

跨关系的筛选条件可以一直延展。

关系也是可逆的。可以在目标 model 上使用源 model 名称的小写形式得到反向关联。

下面这个例子检索至少关联一个 Entry 且大标题 headline 包含 'Lennon' 的所有 Blog 对象:

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

如果在某个关联 model 中找不到符合过滤条件的对象,Django 将视它为一个空的 (所有的值都是 NULL), 但是可用的对象。这意味着不会有异常抛出,在这个例子中:

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

(假设关联到 Author 类), 如果没有哪个 author 与 entry 相关联,Django 会认为它没有 name 属性,而不会因为不存在 author 抛出异常。通常来说,这正是你所希望的机制。唯一的例外是使用 isnull 的情况。如下:

Blog.objects.filter(entry__author__name__isnull=True)

这段代码会得到 authorname 为空的 Blogentryauthor为空的 Blog。 如果不嫌麻烦,可以这样写:

Blog.objects.filter (entry__author__isnull=False,
        entry__author__name__isnull=True)

跨一对多/多对多关系(Spanning multi-valued relationships)

这部分是Django 1.0中新增的: 请查看版本记录

如果你的过滤是基于 ManyToManyField 或是逆向 ForeignKeyField 的,你可能会对下面这两种情况感兴趣。回顾 Blog/Entry 的关系(BlogEntry 是一对多关系),如果要查找这样的 blog:它关联一个大标题包含"Lennon",且在2008年出版的 entry ;或者要查找这样的 blogs:它关联一个大标题包含"Lennon"的 entry ,同时它又关联另外一个在2008年出版的 entry 。因为一个 Blog 会关联多个的Entry,所以上述两种情况在现实应用中是很有可能出现的。

同样的情形也出现在 ManyToManyField 上。例如,如果 Entry 有一个 ManyToManyField 字段,名字是 tags,我们想得到 tags 是"music""bands"entries,或者我们想得到包含名为"music" 的标签而状态是"public"entry

针对这两种情况,Django 用一种很方便的方式来使用 filter()exclude()。对于包含在同一个 filter() 中的筛选条件,查询集要同时满足所有筛选条件。而对于连续的 filter() ,查询集的范围是依次限定的。但对于跨一对多/多对多关系查询来说,在第二种情况下,筛选条件针对的是主 model 所有的关联对象,而不是被前面的 filter() 过滤后的关联对象。

这听起来会让人迷糊,举个例子会讲得更清楚。要检索这样的 blog:它要关系一个大标题中含有 "Lennon" 并且在2008年出版的 entry (这个 entry 同时满足这两个条件),可以这样写:

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

要检索另外一种 blog:它关联一个大标题含有"Lennon"的 entry ,又关联一个在2008年出版的 entry (一个 entry 的大标题含有 Lennon,同一个或另一个 entry 是在2008年出版的)。可以这样写:

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

在第二个例子中,第一个过滤器(filter)先检索与符合条件的 entry 的相关联的所有 blogs。第二个过滤器在此基础上从这些 blogs 中检索与第二种 entry 也相关联的 blog。第二个过滤器选择的 entry 可能与第一个过滤器所选择的完全相同,也可能不同。 因为过滤项过滤的是 Blog,而不是 Entry

上述原则同样适用于 exclude():一个单独 exclude() 中的所有筛选条件都是作用于同一个实例 (如果这些条件都是针对同一个一对多/多对多的关系)。连续的 filter()exclude() 却根据同样的筛选条件,作用于不同的关联对象。

在过滤器中引用 model 中的字段(Filters can reference fields on the model)

这部分是 Django 1.1 新增的: 请查看版本记录

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

例如:要查询回复数(comments)大于广播数(pingbacks)的博文(blog entries),可以构造一个 F() 对象在查询中引用评论数量:

>>> from django.db.models import F
>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。例如,要找到广播数等于评论数两倍的博文,可以这样修改查询语句:

>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2)

要查找阅读数量小于评论数与广播数之和的博文,查询如下:

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

你也可以在 F() 对象中使用两个下划线做跨关系查询。F() 对象使用两个下划线引入必要的关联对象。例如,要查询博客(blog)名称与作者(author)名称相同的博文(entry),查询就可以这样写:

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

主键查询的简捷方式 (The pk lookup shortcut)

为使用方便考虑,Django 用 pk 代表主键"primary key"。

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语句中转义百分号%和下划线_ (Escaping percent signs and underscores in LIKE statements)

字段筛选条件相当于 LIKE SQL 语句 (iexact, contains, icontains, startswith, istartswith, endswithiendswith) ,它会自动转义两个特殊符号 -- 百分号%和下划线_。(在 LIKE 语句中,百分号%表示多字符匹配,而下划线_表示单字符匹配。)

这就意味着我们可以直接使用这两个字符,而不用考虑他们的 SQL 语义。例如,要查询大标题中含有一个百分号%的 entry:

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

Django 会处理转义;最终的 SQL 看起来会是这样:

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

下划线_和百分号%的处理方式相同,Django 都会自动转义。

缓存和查询 (Caching and QuerySets)

每个 QuerySet 都包含一个缓存,以减少对数据库的访问。要编写高效代码,就要理解缓存是如何工作的。

一个 QuerySet 时刚刚创建的时候,缓存是空的。 QuerySet 第一次运行时,会执行数据库查询,接下来 Django 就在 QuerySet 的缓存中保存查询的结果,并根据请求返回这些结果(比如,后面再次调用这个 QuerySet 的时候)。再次运行 QuerySet 时就会重用这些缓存结果。

要牢住上面所说的缓存行为,否则在使用 QuerySet 时可能会给你造成不小的麻烦。例如,创建下面两个 QuerySet ,并对它们求值,然后释放:

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

这就意味着相同的数据库查询将执行两次,事实上读取了两次数据库。而且,这两次读出来的列表可能并不完全相同,因为存在这种可能:在两次读取之间,某个 Entry 被添加到数据库中,或是被删除了。

要避免这个问题,只要简单地保存 QuerySet 然后重用即可:

>>> queryset = Poll.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.

Q 对象实现复杂查找 (Complex lookups with Q objects)

filter() 等函式中关键字参数彼此之间都是 "AND" 关系。如果你要执行更复杂的查询(比如,实现筛选条件的 OR 关系),可以使用 Q 对象。

Q 对象(django.db.models.Q)是用来封装一组查询关键字的对象。这里提到的查询关键字请查看上面的 "Field lookups"

例如,下面这个 Q 对象封装了一个单独的 LIKE 查询:

Q(question__startswith='What')

Q 对象可以用 &| 运算符进行连接。当某个操作连接两个 Q 对象时,就会产生一个新的等价的 Q 对象。

例如,下面这段语句就产生了一个 Q ,这是用 "OR" 关系连接的两个 "question__startswith" 查询:

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查询实例(OR lookups examples) 中展示了 Q 的用例。

对象比较 (Comparing objects)

要比较两个对象,就和 Python 一样,使用双等号运算符:==。实际上比较的是两个 model 的主键值。

以上面的 Entry 为例,下面两个语句是等价的:

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

如果 model 的主键名称不是 id,也没关系。Django 会自动比较主键的值,而不管他们的名称是什么。例如,如果一个 model 的主键字段名称是 name,那么下面两个语句是等价的:

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

对象删除(Deleting objects)

删除方法就是 delete()。它运行时立即删除对象而不返回任何值。例如:

e.delete()

你也可以一次性删除多个对象。每个 QuerySet 都有一个 delete() 方法,它一次性删除 QuerySet 中所有的对象。

例如,下面的代码将删除 pub_date 是2005年的 Entry 对象:

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

要牢记这一点:无论在什么情况下,QuerySet 中的 delete() 方法都只使用一条 SQL 语句一次性删除所有对象,而并不是分别删除每个对象。如果你想使用在 model 中自定义的 delete() 方法,就要自行调用每个对象的delete 方法。(例如,遍历 QuerySet,在每个对象上调用 delete()方法),而不是使用 QuerySet 中的 delete()方法。

在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:

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

要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:

Entry.objects.all().delete()

一次更新多个对象 (Updating multiple objects at once)

这部分是 Django 1.0 中新增的: 请查看版本文档

有时你想对 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() 方法也是即时生效,不返回任何值的(与 delete() 相似)。 在 QuerySet 进行更新时,唯一的限制就是一次只能更新一个数据表,就是当前 model 的主表。所以不要尝试更新关联表和与此类似的操作,因为这是不可能运行的。

要小心的是: update() 方法是直接翻译成一条 SQL 语句的。因此它是直接地一次完成所有更新。它不会调用你的 model 中的 save() 方法,也不会发出 pre_savepost_save 信号(这些信号在调用 save() 方法时产生)。如果你想保存 QuerySet 中的每个对象,并且调用每个对象各自的 save() 方法,那么你不必另外多写一个函式。只要遍历这些对象,依次调用 save() 方法即可:

for item in my_queryset:
    item.save()
这部分是在 Django 1.1 中新增的: 请查看版本文档

在调用 update 时可以使用 F() 对象 来把某个字段的值更新为另一个字段的值。这对于自增记数器是非常有用的。例如,给所有的博文 (entry) 的广播数 (pingback) 加一:

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

但是,与 F() 对象在查询时所不同的是,在filterexclude子句中,你不能在 F() 对象中引入关联关系(NO-Join),你只能引用当前 model 中要更新的字段。如果你在 F() 对象引入了Join 关系object,就会抛出 FieldError 异常:

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

直接使用SQL(Falling back to raw SQL)

如果你发现某个 SQL 查询用 Django 的数据库映射来处理会非常复杂的话,你可以使用直接写 SQL 来完成。

建议的方式是在你的 model 自定义方法或是自定义 model 的 manager 方法来运行查询。虽然 Django 不要求数据操作必须在 model 层中执行。但是把你的商业逻辑代码放在一个地方,从代码组织的角度来看,也是十分明智的。详情请查看 执行原生SQL查询(Performing raw SQL queries).

最后,要注意的是,Django的数据操作层仅仅是访问数据库的一个接口。你可以用其他的工具,编程语言,数据库框架来访问数据库。对你的数据库而言,没什么是非用 Django 不可的。

Questions/Feedback

Having trouble? We'd like to help!