执行查询
一旦创建 数据模型 后,Django 自动给予你一套数据库抽象 API,允许你创建,检索,更新和删除对象。本页介绍如何使用这些 API。参考 数据模型参考 获取所有查询选项的完整细节。
在本指南中(以及相关参考资料中),我们将引用以下模型,这些模型包含了一个 Webblog 应用:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
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()
才操作数据库。
方法二:
b=Blog.objects.create(name='Beatles Blog', tagline='All the latest Beatles news.')
一步到位,不用调用save()
将修改保存至对象
要将修改保存至数据库中已有的某个对象,使用 save()
。
有一个已被存入数据库中的 Blog
实例 b5
,本例将其改名,并在数据库中更新其记录:
>>> b5.name = 'New name'
>>> b5.save()
这在幕后执行了 UPDATE
SQL 语句。Django 在你显示调用 save()
后才操作数据库。
保存 ForeignKey 和 ManyToManyField 字段
更新 ForeignKey
字段的方式与保存普通字段的方式相同——只需将正确类型的实例分配给相关字段。本例为 Entry
类的实例 entry
更新了 blog
属性,假设 Entry
和 Blog
的实例均已保存在数据库中(因此能在下面检索它们):
>>> from blog.models import Blog, 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 会在添加或指定错误类型的对象时报错。\
BlackLivesMatter
-
语言: zh-hans
-
文档版本: 3.0
执行查询¶
一旦创建 数据模型 后,Django 自动给予你一套数据库抽象 API,允许你创建,检索,更新和删除对象。本页介绍如何使用这些 API。参考 数据模型参考 获取所有查询选项的完整细节。
在本指南中(以及相关参考资料中),我们将引用以下模型,这些模型包含了一个 Webblog 应用:
<pre>from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
</pre>
创建对象¶
为了用 Python 对象展示数据表对象,Django 使用了一套直观的系统:一个模型类代表一张数据表,一个模型类的实例代表数据库表中的一行记录。
要创建一个对象,用关键字参数初始化它,然后调用 save()
将其存入数据库。
假设模型都位于文件 mysite/blog/models.py
中,这是一个例子:
<pre>>>> from blog.models import Blog
b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
b.save()
</pre>
这在幕后执行了 INSERT
SQL 语句。Django 在你显式调用 save()
才操作数据库。
save()
方法没有返回值。
参见
save()
接受很多此处未介绍的高级选项。参考文档 save()
获取完整细节。
要一步创建并保存一个对象,使用 create()
方法。
将修改保存至对象¶
要将修改保存至数据库中已有的某个对象,使用 save()
。
有一个已被存入数据库中的 Blog
实例 b5
,本例将其改名,并在数据库中更新其记录:
<pre>>>> b5.name = 'New name'
b5.save()
</pre>
这在幕后执行了 UPDATE
SQL 语句。Django 在你显示调用 save()
后才操作数据库。
保存 ForeignKey
和 ManyToManyField
字段¶
更新 ForeignKey
字段的方式与保存普通字段的方式相同——只需将正确类型的实例分配给相关字段。本例为 Entry
类的实例 entry
更新了 blog
属性,假设 Entry
和 Blog
的实例均已保存在数据库中(因此能在下面检索它们):
<pre>>>> from blog.models import Blog, Entry
entry = Entry.objects.get(pk=1)
cheese_blog = Blog.objects.get(name="Cheddar Talk")
entry.blog = cheese_blog
entry.save()
</pre>
更新 ManyToManyField
字段有点不同——在字段上使用 add()
方法为关联关系添加一条记录。本例将 Author
实例 joe
添加至 entry
对象:
<pre>>>> from blog.models import Author
joe = Author.objects.create(name="Joe")
entry.authors.add(joe)
</pre>
要一次添加多行记录至 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
代表来自数据库中对象的一个集合。它可以有 0 个,1 个或者多个 filters. Filters,可以根据给定参数缩小查询结果量。在 SQL 的层面上, QuerySet
对应 SELECT
语句,而filters对应类似 WHERE
或 LIMIT
的限制子句。
你能通过模型的 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()
返回了一个 QuerySet
,后者包含了数据库中所有的 Blog
对象。
检索全部对象
从数据库中检索对象最简单的方式就是检索全部。为此,在 Manager
上调用 all()
方法:
>>> all_entries = Entry.objects.all()
方法 all()
返回了一个包含数据库中所有对象的 QuerySet
对象。
通过过滤器检索指定对象
all()
返回的 QuerySet
包含了数据表中所有的对象。虽然,大多数情况下,你只需要完整对象集合的一个子集。
要创建一个这样的子集,你需要通过添加过滤条件精炼原始 QuerySet
。两种最常见的精炼 QuerySet
的方式是:
<dl class="docutils">
<dt>filter(**kwargs)
</dt>
<dd>返回一个新的 QuerySet
,包含的对象满足给定查询参数。</dd>
<dt>exclude(**kwargs)
</dt>
<dd>返回一个新的 QuerySet
,包含的对象 不 满足给定查询参数。</dd>
</dl>
查询参数(**kwargs
)应该符合下面的 Field lookups 的要求。
例如,要包含获取 2006 年的博客条目(entries blog)的 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.date(2005, 1, 30)
... )
这个先获取包含数据库所有条目(entry)的 QuerySet
,然后排除一些,再进入另一个过滤器。最终的 QuerySet
包含标题以 "What" 开头的,发布日期介于 2005 年 1 月 30 日与今天之间的所有条目
每个 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
是今天或未来的所有记录。最初的 QuerySet
(q1
) 不受筛选操作影响。
用 get() 检索单个对象
filter()
总是返回一个 QuerySet
,即便只有一个对象满足查询条件 —— 这种情况下, QuerySet
只包含了一个元素。
若你知道只会有一个对象满足查询条件,你可以在 Manager
上使用 get()
方法,它会直接返回这个对象:
>>> one_entry = Entry.objects.get(pk=1)
你可以对 get()
使用与 filter()
类似的所有查询表达式 —— 同样的,参考下面的 Field lookups。
注意, 使用切片 [0]
时的 get()
和 filter()
有点不同。如果没有满足查询条件的结果, get()
会抛出一个 DoesNotExist
异常。该异常是执行查询的模型类的一个属性 —— 所有,上述代码中,若没有哪个 Entry
对象的主键是 1,Django 会抛出 Entry.DoesNotExist
。
类似了,Django 会在有不止一个记录满足 get()
查询条件时发出警告。这时,Django 会抛出 MultipleObjectsReturned
,这同样也是模型类的一个属性。
其它 QuerySet 方法
大多数情况下,你会在需要从数据库中检索对象时使用 all()
, get()
, filter()
和 exclude()
。然而,这样远远不够;完整的各种 QuerySet
方法请参阅 QuerySet API 参考。
限制 QuerySet 条目数
利用 Python 的数组切片语法将 QuerySet
切成指定长度。这等价于 SQL 的 LIMIT
和 OFFSET
子句。
例如,这将返回前 5 个对象 (LIMIT 5
):
>>> Entry.objects.all()[:5]
这会返回第 6 至第 10 个对象 (OFFSET 5 LIMIT 5):
>>> Entry.objects.all()[5:10]
不支持负索引 (例如 Entry.objects.all()[-1]
)
一般情况下, QuerySet
的切片返回一个新的 QuerySet
—— 其并未执行查询。一个特殊情况是使用了的 Python 切片语法的 “步长”。例如,这将会实际的执行查询命令,为了获取从前 10 个对象中,每隔一个抽取的对象组成的列表:
>>> Entry.objects.all()[:10:2]
由于对 queryset 切片工作方式的模糊性,禁止对其进行进一步的排序或过滤。
要检索 单个 对象而不是一个列表时(例如 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';
查询子句中指定的字段必须是模型的一个字段名。不过也有个例外,在 ForeignKey
中,你可以指定以 _id
为后缀的字段名。这种情况下,value 参数需要包含 foreign 模型的主键的原始值。例子:
>>> 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 查询是最常见的。
不分大小写的匹配,查询语句:
>>> Blog.objects.get(name__iexact="beatles blog")
会匹配标题为 "Beatles Blog", "beatles blog", 甚至 "BeAtlES blOG" 的 Blog。
大小写敏感的包含测试。例子:
Entry.objects.get(headline__contains='Lennon')
粗略地转为 SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
注意这将匹配标题 'Today Lennon honored'
,而不是 'today lennon honored'
。
这也有个大小写不敏感的版本, icontains
。
<dt>startswith
, endswith
</dt>
<dl class="docutils">
<dd>以……开头和以……结尾的查找。当然也有大小写不敏感的版本,名为 istartswith
和 iendswith
。</dd>
</dl>
同样,这只介绍了皮毛。完整的参考能在 field 查询参考 找到。
网友评论