构造查询¶
一旦创建完 数据模型 , Django 会自动给你一套数据库抽象的 API ,以用于创建、检索、更新和删除对象。本文为你介绍如何使用这套 API 。所有不同种类的搜索参数详见数据模型参考 。
本文所有内容(包括本手册)全部围绕以下模型展开,这些模型组成了一个博客应用:
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() mod_date = models.DateTimeField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __unicode__(self): return self.headline
创建对象¶
为在 Python 对象中表现数据库表中的数据, Django 使用一个非常直观的系统:一个模型类表现一个数据库表,并且一个该类的实例表现表中一个指定的记录。
要创建一个对象,只要通过使用关键参数实例化模型类,然后调用 save() 把对象保存到数据库就行了。
你可以从任何 Python 路径导入模型类。(我们之所以要指出这点是因为旧版本 Django 导入模型没有这么方便。)
假设模型存在于文件 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() 方法没有返回值。
See also
save() 有许多高级参数这里没有提到,详见其文档。
要一步完成创建并且同时保存对象参见 `create()` 方法。
修改对象¶
要修改数据库中已存在的对象,请使用 save() 。
假设一个 Blog 的实例 b5 ,这个实例已经保存进了数据库,以下例子会修改其 name 并且会修改数据库中相应的数据:
>> b5.name = 'New name'
>> b5.save()
上例在后执行了一个 UPDATE SQL 语句。 Django 不会执行数据库操作,除非显式调用save() 。
保存 外键 和 多对多字段 字段¶
更新一个 外键 字段的方法与一般的字段一样,只要设置正确的字段类型就可以了。本例更新了一个Entry 实例的blog 属性:
>>> 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()
更新一个 多对多字段 稍有不同,要对更新的字段使用add() 方法来为关系增加一个记录。本例在entry 对象中加入了Author 实例 joe
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
如果你尝试使用不正确的对象类型,那么 Django 会有意见的。
获取对象¶
要从你的数据库中获得对象,你必须通过你的模型类的 管理器(Manager) 来构建一个 查询集(QuerySet ) 。
一个 查询集 表现为数据库中对象的集合。它可以没有、有一个或有多个过滤器( filters ) 。过滤器是限制集合范围的准则。在 SQL 语句中,一个查询集 等于一个 SELECT 语句,一个过滤器相当于一个WHERE 或LIMIT 子句。
你可以通过使用模型的 管理器 得到一个查询集 。每个模型至少有一个管理器 ,``管理器`` 一般也称作 对象(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."
Note
管理器 只能由模型类操作,而不能由模型实例操作。这样用以区别表级别 操作和记录级别 操作。
管理器 是一个模型的 查询集 的主要来源,它类似于模型的数据库表中所有对象的“根” 查询集 。例如, Blog.objects 是包含Blog 数据库表中所有对象的初始查询集 。
获得所有对象¶
从数据库表中获得对象的最简单的方法就是获得所有对象。在一个 管理器 上使用all() 方法可以获得所有对象:
>>> all_entries = Entry.objects.all()
all() 方法返回一个包含所有数据库表中对象的查询集 。
(既然 Entry.objects 是一个查询集 ,为什么不使用Entry.objects ?这是因为 Entry.objects ,是一个根查询集 ,是一个不能表现出来的东西。all() 方法返回的是 可以 表现出来的查询集 。
用过滤器获得特定对象¶
根 查询集 通过 管理器 提供数据库表的所有对象的描述。但是通常你只需要全部对象的一个子集。
要得到这个子集,就必须改进原始的 查询集 ,增加一些过滤条件,通常使用以下两种方法:
-
filter(**kwargs)
- 返回一个新的包含符合查询条件对象的 查询集 。 exclude(**kwargs)
- 返回一个新的 不 包含符合查询条件对象的 查询集 。
查询参数(即上例中的 **kwargs )应当遵循下文字段查找 一节中描述的规则。
例如,要获得一个包含所有 2006 年的博客条目的 查询集 ,可以这样使用filter()
Entry.objects.filter(pub_date__year=2006)
这里我们不需要添加一个 all() --Entry.objects.all().filter(...) 。当然添加了也可以,但是一般只有在需要从根查询集 中获取所有对象时才使用 all() 。
连环过滤器¶
过滤器 可以连环使用,例如:
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.now()
... ).filter(
... pub_date__gte=datetime(2005, 1, 1)
... )
上例中在原始的包含所有记录的 查询集 上加上了一个过滤器,再加上了一个排除,最后再加上了一个过滤器。最后得到的是一个 headline 以 "What" 开头的,发布时间介于 2005 年 1 月 1 日和当前时间之间的查询集 。
过滤后的查询集是独立唯一的¶
每一次提取的 查询集 都是独立的、唯一的,是与前一次的查询集 无关的,是可以储存、使用和再使用的。
例如:
>> q1 = Entry.objects.filter(headline__startswith="What")
>> q2 = q1.exclude(pub_date__gte=datetime.now())
>> q3 = q1.filter(pub_date__gte=datetime.now())
以上三个 查询集 是独立的。第一个是一个基础查询集 ,包含所有 headline 以 "What" 开头的记录。第二个查询集 是第一个的子集,去除了发布时间晚于当前时间的记录。第三个查询集 是第一个的子集,只含有发布时间晚于当前时间的记录。原始的查询集 (q1 )不会因为后面的添加限制条件而改变。
查询集是惰性的¶
查询集 是惰性的,即创建一个 查询集 不会触动数据库。你可以不停地创建过滤器,但 Django 不会真正运行查询,除非 查询集 被 执行 了。来看以下的例子:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.now())
>>> q = q.exclude(body_text__icontains="food")
>>> print q
上例中表面看执行了三个数据库查询,但实质只有最后一行代码( printq )执行了一次数据库查询。通常,只到你“要求”返回结果时,查询集 才会从数据库中抓取数据, 查询集 被执行 了。更多关于执行的精确时间的内容参见 When QuerySets are evaluated 。
使用 get 获取一个单一的对象¶
.filter() 总是给你一个 查询集 ,即使只有一个对象符合查询条件也会返回只包含一个对象的 查询集 。
如果你确定你的查询只有一个符合条件的对象,那么你可以使用在 管理器 上使用 get() 方法直接获取这个对象:
>>> one_entry = Entry.objects.get(pk=1)
你可以象 filter() 一样在 get() 上使用任何查询表达式。参见下文的 字段查找 。
注意, filter() 和 get() 有一点不同。如果没有找到符合条件的对象,那么 .get() 会抛出一个 DoesNotExist 异常。这个异常是被执行查询的模型类的一个属性。在上面的例子中,如果没有这键为 1 的Entry ,那么 Django 会抛出Entry.DoesNotExist 异常。
同样,如果多个对象符合条件, Django 也会不爽,会抛出 MultipleObjectsReturned 异常。这个异常也是模型类的一个属性。
其他查询集方法¶
大多数时间,当需要在数据库中查找对象时会使用 all() 、get() 、filter() 和 exclude() 。但是,这还是远远不够的。要得到所有查询集 方法的完整列表,请参阅查询集 API 手册 。
限制查询集¶
可以使用 Python 序列切片语法的子集来限制你的 查询集 。这将相当于 SQL 的LIMIT 和OFFSET 子句。
例如,下例返回前五个对象( LIMIT 5 ):
>>> Entry.objects.all()[:5]
下例返回第六到第十个对象( OFFSET 5 LIMIT 5 ):
>>> Entry.objects.all()[5:10]
负数索引(如: Entry.objects.all()[-1] )是不支持的。
通常,对一个 查询集 切片只会返回一个新的查询集 ,不会执行这个查询 。但是有一个例外:假如使用 Python 语法中的“步长”参数,那么就会执行查询。下例用于返回前十个对象中的第偶数个对象,查询会立即执行:
>>> Entry.objects.all()[:10:2]
要得到一个 单一 的对象而不是一个列表(如: SELECTfooFROM barLIMIT1 ),可以使用一个简单的索引,而不使用切片。例如下例返回数据中按字母排序后的第一条记录:
>>> Entry.objects.order_by('headline')[0]
上例基本上等同于:
>>> Entry.objects.order_by('headline')[0:1].get()
注意:当查询结果为空时,第一个例子会产生 IndexError 异常,而第二个例子则产生DoesNotExist 异常。详见get() 。
字段查找¶
字段查找的定义相当于书写 SQL 的 WHERE 子句。定义方法是把条件作为查询集 的filter() 、 exclude() 和get() 方法的关键字参数。
基本的查找关键字参数形如: 字段__查找类型=值 (注意:是双下划线)。例如:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
翻译为 SQL 语句大致如下:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
这是如何做到的
Python 中的函数可以接受独特的名称-值参数,并且这些参数可以动态赋值。更详细的信息参见官方 Python 教程的 关键字参数 。
如果你传递了一个非法的关键字参数,会产生 TypeError 。
查找类型约有二十多个,详见 字段查找手册 。以下是一些常用的查找例子:
-
exact
-
“精确”匹配。例如:
>>> Entry.objects.get(headline__exact="Man bites dog")
相当于以下 SQL 语句:
SELECT ... WHERE headline = 'Man bites dog';
如果没有定义查找类型,即没有使用双下划线,那么默认类型为 exact 。
例如以下两句是等价的:
>>> Blog.objects.get(id__exact=14) # 显式声明 >>> Blog.objects.get(id=14) # __exact 隐含在内了
这样做是为了方便,因为 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
- 大小写敏感的匹配开头和结尾。相应大小写不敏感的是 istartswith 和 iendswith 。
再次重申,以下介绍只是一些皮毛。完整的手册详见 字段查找手册 。
跨关系查找¶
Django 提供一个有力且直观的方法来“追踪”查找中的关系,即会在后台自动处理 SQL 中的 JOIN 。要跨越一个关系,只要使用跨模块的关联字段即可,并用两个下划线分隔,直到得到你想要的字段为止。
以下例子可以获得名称为 'Beatles Blog' 的 Blog 的所有条目。
>>> Entry.objects.filter(blog__name__exact='Beatles Blog')
这种跨越的深度是无限的。
跨越也可以是反向的。要指向一个“反向”的关系,请使用模型的小写名称。
下例可以获得至少一个条目的头条包含 'Lennon' 的所有Blog 对象:
>>> Blog.objects.filter(entry__headline__contains='Lennon')
如果你跨越了多重关系并且其中的一个模型没有符合条件的值,那么 Django 会把模型视作空对象(即所有值为 NULL ),而不是非法对象。因此在前述情况下不会引发错误。例如:
Blog.objects.filter(entry__authors__name='Lennon')
假设有一个相关联的 作者``( Author )模型,如果没有 ``作者 与任何一个条目关联,那么就会视作也没有名称 与之关联。这样处理比因为作者 缺失而引发错误要好。通常这也是你想要的处理方式。唯一会产生混淆的是当你使用isnull 的时候。就象下例:
Blog.objects.filter(entry__authors__name__isnull=True)
会返回包括 名字 为空的 作者 和 作者 为空的条目 两种情况的Blog 对象。如果你不希望包括后一种情况的对象,你可以这样写:
Blog.objects.filter(entry__authors__isnull=False,
entry__authors__name__isnull=True)
跨越多值关系¶
当你基于一个 多对多字段 对象或一个反向外键 过滤时,有两种不同的过滤方法值得注意。对于博客 与条目 关系( 博客 对于条目 是一对多关系),我们可能会想要查找一个博客,这个必须博客包括一个符合以下条件的条目:在条目的头条中包含"Lennon" 字样,并且发布日期为 2008 年。或者我们也可以会想要查找一个博客,这个博客必须符合以下条件:包含一个头条中有"Lennon" 字样的条目,并且包含一个发布日期为 2008 年的条目。因为一个博客 对应于多个条目 ,所以上述两种查询都是可能的,在特定情况下都是有用的。
同样,一个 多对多字段 也会引发类似情况。例如,假设一个条目 字段有一个名为标记 ( tags )的多对多字段。我们可以会想要查找既有"music" 标记又有"bands" 标记的条目。或者我们也可能会想要查找标记为 "music" ,状态为"public" 的条目。
对于上述两种方法, Django 中的 filter() 和exclude() 使用相同的方式来处理。在一个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)
在第二个例子中,第一个过滤器限定了特定条件的博客,第二个过滤器 进一步 限定了同时符合第二个条件的博客。匹配第二个过滤器的条目不一定匹配第一个过滤器。两个过滤器的条件都是针对某个博客而言的,而不是针对某个条目而言的。
以上方式也同样用于 exclude() 。
过滤器可以引用模型中的字段¶
迄今为止的例子中,我们构建的过滤器都是比较字段和常量的,但是如果我们要比较同一模型中的两个不同字段怎么办?
Django 提供了 F() 对象来做这样在比较。F() 对象查询中可以作为模型字段的引用,这样就可以在查询中比较同一模型实例中的不同字段了。
例如,我们要查找评论比广播( pingback )多的条目,那么先构建一个引用评论数量的 F() 对象,然后在查询中使用这个F() 对象:
>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
Django 支持 F() 对象的加减乘除和取模运算。要查找广播数为评论数两倍的条目可以使用如下查询:
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
要查找广播数和评论数之和大于级别的条目可以使用如下查询:
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
你也可以在 F() 对象中使用双下划线来跨越关系。带有双下划线的F() 对象会自动使用必须联合。例如查找所有作者为博客名称的条目,可以使用如下查询:
>>> Entry.objects.filter(authors__name=F('blog__name'))
对于日期和日期时间字段你可以加上或减去一个 datetime.timedelta 对象。下例可以返回所有在发布三天后修改过条目:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
pk 缩写¶
为了方便, Django 提供了一个 pk 缩写来代表“主键”。
在博客模型例子中,主键是 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 查询,任何查询短语都可以:
# 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 语句的字段查找(iexact 、contains 、 icontains 、startswith 、istartswith 、 endswith 和iendswith )中的百分号和下划线会被自动转义。(在LIKE 语句中,百分号是一个代表多个字符的通配符,下划线是一个代表单个字符的通配符。)
这种方式使用语句看起来比较直观。例如,要查询所有包含百分号的条目,可以象对待其它字符一样对待百分号:
>>> Entry.objects.filter(headline__contains='%')
Django 会帮你处理引号,上例的相应的 SQL 如下:
SELECT ... WHERE headline LIKE '%\%%';
对下划线的处理方法与之相同。这样可以直观地处理百分号和下划线。
缓存和查询集¶
每个 查询集 都包含一个缓存,用于最小化数据库使用。为了写出最有效率的代码,理解缓存的工作方式是非常重要的。
当一个 查询集 新建时,其缓存是空的。当查询集 第一次被执行时,数据库就会使用一次, Django 会把查询结果保存在查询集 的缓存中并返回结果。以后的查询就会重复使用缓存。
你要小心缓存的行为,如果不正确使用 查询集 ,那么有可能上当。例如下列语句会创建两个查询集 并执行它们,然后把它们抛在一边:
>>> print [e.headline for e in Entry.objects.all()]
>>> print [e.pub_date for e in Entry.objects.all()]
这意味着两个同样的查询被执行了两次,浪费啊。同时,两条语句的条目还可能不一致,因为在两次查询之间可能会有条目增减。
要避免以上问题,只要保存 查询集 并且重复使用就可以了:
>>> queryset = Entry.objects.all()
>>> print [p.headline for p in queryset] # 执行查询集。
>>> print [p.pub_date for p in queryset] # 重复使用缓存。
使用 Q 对象进行复杂查询¶
在 filter() 等语句中的关键字查询参数都是“逻辑与”的关系。如果要进行更复杂的查询(如条件之间为“逻辑或”的关系),那么可以使用Q 对象。
Q 对象( django.db.models.Q )是用于封装一堆关键字查询参数的对象。这些关键字查询参数在上文“字段查找”中已描述过。
例如,下面的 Q 对象封装了一个单一的LIKE 查询:
from django.db.models import Q
Q(question__startswith='What')
Q 对象可以用 & 和 | 操作符联结,联结之后会产生一个新的Q 对象。
下例产生了一个新的 Q 对象,这个对象表示两个"question__startswith" 查询的OR 的联结:
Q(question__startswith='Who') | Q(question__startswith='What')
上例相当于下面的 SQL WHERE 子句:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
可以用 & 和 | 操作符及圆括号来组合任意复杂的 Q 对象,还可以使用~ 符号来表示“逻辑非”。例如:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
每个有关键字参数的查询函数(如 filter() 、``exclude()`` 和get() )可以接受一个或多个Q 对象作为位置参数(不是命名参数)。如果你提供了多个Q 对象,则参数之间是“逻辑与”的关系。例如:
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 对象和关键字参数)都是“逻辑与”的关系。如果混合使用这两种参数,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)))
... 是不合法的。
See also
Django 单元测试中的 逻辑或查询例子 中有一些 Q 对象的使用例子。
比较对象¶
使用标准的 Python 比较操作符双等于号 == 来比较两个模型实例。这种比较在后台实质是比较两个模型的主键的值。
在博客条目的例子中,以下两个语句是等价的:
>>> 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()
也可以批量删除对象。每个 查询集 都有一个删除所有成员的delete() 方法。
例如以下例子删除所有 pub_date 为 2005 年的条目:
Entry.objects.filter(pub_date__year=2005).delete()
请记住,批量删除对象方法是直接执行 SQL 语句的,不会执行模型中的针对每个单独实例的 delete() 方法。如果你要执行自定义的delete() 方法,那么必须“手动” 删除。(例如遍历一个查询集 ,对每个单独的对象执行 delete() 方法。)
当 Django 删除一个对象,缺省情况下它会模拟 SQL 的约束 ONDELETECASCADE ,换句话说,任何有外键指向被删除对象的对象都会同时被删除。例如:
b = Blog.objects.get(pk=1)
# 以下语句会删除博客和它所有的条目。
b.delete()
注意, delete() 方法是唯一没有暴露给“管理器”的查询集 方法。这是一个保护机制,以防止你意外地使用了Entry.objects.delete() ,删除了所有 条目。如果你确实要删除所有对象,那么必须显式地使用如下语句:
Entry.objects.all().delete()
一次更新多个对象¶
有时需要把一个 查询集 中所有对象的某个字段的值设置为同一个特定值,你可以使用update() 方法。例如:
# 更新所有 pub_date 为 2007 年的条目的头条。
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
这个方法只能用于更新非关系型字段和 外键 字段。要更新非关系字段,应当提供一个常量。要更新外键 字段,应当把指向的新模型实例赋于新值。例如:
>>> b = Blog.objects.get(pk=1)
# 改变属于这个博客的所有条目
>>> Entry.objects.all().update(blog=b)
update() 方法是立即执行的并且返回受到查询影响的行数。唯一的限制是只能更新一个数据库的表,即模型的主表。你可以使用基于关系字段的过滤器,但只能更新模型主表中的列。例如:
>>> 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 语句,它只是对 SQL 直接更新的封装。它不会运行任何模型中save() 方法,也不会发出pre_save 或 post_save 信号(这些信号是调用save() 方法而产生的)。如果要保存查询集 中的每一项,并确保每个实例都调用了 save() 方法,那么不需要任何特殊的函数来处理,只要遍历每个实例并调用 save()
for item in my_queryset:
item.save()
也可以通过使用 F() 对象 来基于模型中的其他字段的值来更新。这种方法对于基于字段当前值来增加计数特别有用。例如,要给博客中的每个条目增加广播的计数,可以这样写:
>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
然而,与在过滤器和排除子句中的 F() 对象不同,在使用F() 对象更新时不能引入联合。你只能引用被更新模型本身的字段。否则会引发一个FieldError
# 这会引发一个 FieldError
>>> Entry.objects.update(headline=F('blog__name'))
回退到原始 SQL¶
如果你觉得 Django 的数据库映射用起来太复杂了,那么你可以手工撰写 SQL 语句。 Django 可以为原始 SQL 语句提供许多选择,参见 执行原始 SQL 查询 。
最后,要注意重要的一点: Django 的数据库层只是数据库的一个接口。你可通过其他工具、编程语言或数据库框架来操作数据库, Django 没有强制性的要求。