Skip to content
tulpar edited this page Apr 13, 2015 · 1 revision

Date: 2014-09-07
Title: Django 数据查询
Tags: Django
Category: It

class Poll(models.Model):
	slug = models.SlugField(unique_for_month='pub_date')
	question = models.CharField(maxlength=255)
	pub_date = models.DateTimeField()
	expire_date = models.DateTimeField()

	def __repr__(self):
	return self.question

	class Meta:
	get_latest_by = 'pub_date'





class Choice(models.Model):
	poll = models.ForeignKey(Poll, edit_inline=models.TABULAR,
	num_in_admin=10, min_num_in_admin=5)
	choice = models.CharField(maxlength=255, core=True)
	votes = models.IntegerField(editable=False, default=0)

	def __repr__(self):
	return self.choice

获得一个数据对象p1

from datetime import datetime
p1 = Poll(slug='whatsup', question="What's up?",\
	pub_date=datetime(2005, 2, 20), expire_date=datetime(2005, 4, 20))
p1.save()
  • 数据对象有一个初始方法save()

获取结果集对象

无限制获取对象集p2

p2=Poll.objects.all()
>>>p2
[What's up?, What's your name?]
注意:在这里p2是个对象集,自身也是个对象。

增加一些限制条件直到描述的子集满足你的需要。

最常用的两个定制结果集的方法是:

filter(**kwargs)
返回一个匹配查询参数的新的结果集.
exclude(**kwargs)
返回一个不匹配查询参数的新的结果集.

这两个方法的返回值都是结果集对象,因此结果集可以进行链式处理:

Poll.objects.filter(question__startswith="What")\
	.exclude(pub_date__gte=datetime.now())\
	.filter(pub_date__gte=datetime(2005,1,1))

以一个初始结果集作为参数, 然后进行过滤, 再进行排除, 再进行另一个过滤.
这样得到的最终结果就一个问题开头单词是 "What", 发布日期在 2005年1月1日至今的所有民意测验的集合.

每个结果集都是一个独一无二的对象. 以上操作的每一步都生成了一个新的结果集:


q1 = Poll.objects.filter(question__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.now())
q3 = q1.filter(pub_date__gte=datetime.now())

这三步生成了三个结果集; 一个初始结果集包含所有的以"What"开头的民意测验, 两个初始结果集的子集(一个排除条件,一个过滤条件).
对原始结果集的改进过程并没有影响到原始的结果集.

值得注意的是结果集的创建根本没有访问数据库.只有当对结果集取值时才会访问数据库.

字段查询

以 field__lookuptype (注意是双下线)形式进行基本的字段查询,举例来说:

polls.objects.filter(pub_date__lte=datetime.now())

该查询翻译成SQL就是:

SELECT * FROM polls_polls WHERE pub_date <= NOW();

DB API 支持下列查找类型:

类型 描述
exact 精确匹配: polls.get_object(id__exact=14).
iexact 忽略大小写的精确匹配: polls.objects.filter(slug__iexact="foo") 匹配 foo, FOO, fOo, 等等.
contains 大小写敏感的内容包含测试: polls.objects.filter(question__contains="spam") 返回question 中包含 "spam" 的所有民意测验.(仅PostgreSQL 和 MySQL支持. SQLite 的LIKE 语句不支持大小写敏感特性. 对Sqlite 来说, contains 等于 icontains.)
icontains 大小写不敏感的内容包含测试:
gt 大于: polls.objects.filter(id__gt=4).
gte 大于等于.
lt 小于.
lte 小于等于.
ne 不等于.
in 位于给定列表中: polls.objects.filter(id__in=[1, 3, 4]) 返回一个 polls 列表(ID 值分别是 1或3或4).
startswith 大小写敏感的 starts-with: polls.objects.filter(question__startswith="Would").(仅PostgreSQL 和MySQL支持. SQLite 的LIKE 语句不支持大小写敏感特性. 对Sqlite 来说,``startswith`` 等于 istartswith)
endswith 大小写敏感的 ends-with. (仅PostgreSQL 和 MySQL)
istartswith 大小写不敏感的 starts-with.
iendswith 大小写不敏感的 ends-with.
range 范围测试: polls.objects.filter(pub_date__range=(start_date, end_date)) 返回 pub_date 位于 start_date 和 end_date (包括)之间的所有民意测验
year 对 date/datetime 字段, 进行精确的 年 匹配: polls.get_count(pub_date__year=2005).
month 对 date/datetime 字段, 进行精确的 月 匹配:
day 对 date/datetime 字段, 进行精确的 日 匹配:
isnull True/False; 做 IF NULL/IF NOT NULL 查询: polls.objects.filter(expire_date__isnull=True).
如果未提供查找类型, 系统就认为查找类型是 exact . 下面两个语句是等价的:
Poll.objects.get(id=14)
Poll.objects.get(id__exact=14)
查询允许多个条件参数, 逗号分隔的多个条件参数会被 "AND" 起来使用:
polls.objects.filter(
    pub_date__year=2005,
    pub_date__month=1,
    question__startswith="Would",
)

得到2005年1月公布的带有一个"Would"开头的问题的所有民意测验.

为了使用更加方便, 还提供有一个 pk 查找类型, 可以翻译成 (primary_key)__exact.

在这个民意测试的例子里, 下面两个语句是等价的.:

polls.get_object(id__exact=3)
polls.get_object(pk=3)
pk 也可以通过连接进行查询.

在这个民意测试的例子里, 下面两个语句是等价的:

choices.objects.filter(poll__id__exact=3)
choices.objects.filter(poll__pk=3)

如果传递的关键字参数非法, 将引发 TypeError 异常.

OR 查询

关键字参数查询的各个条件都是 "AND" 关系. 如果你需要一个复杂的查询(举例来说,你需要一个 OR 语句), 你需要使用 Q 对象.
Q 对象是 django.core.meta.Q 的实例, 用来装载一系列关键字参数. 这些关键字参数就象指定给 get() 和 filter() 函数的关键字参数一样. 举例来说:

Q(question__startswith='What')

Q 对象可以使用 & 和 | 运算符进行组合. 当两个Q对象进行 & 或 | 运算时,会生成一个新的Q对象.举例来说语句:

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

生成一个新的 Q 对象表示这两个 "question__startswith" 查询条件的 "OR" 关系. 等同于下面的 SQL WHERE 子句:

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

查询函数可以接受一个或多个 Q 对象作为参数.如果提供有多个 Q 对象参数, 它们将被 "AND" 到一起. 举例来说:

polls.get_object(
Q(question__startswith='Who'),
Q(pub_date__exact=date(2005, 5, 2)) | Q(pub_date__exact=date(2005, 5, 6))
)

翻译成 SQL 就是这样:

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

从结果集中取值

只有通过取值操作才能得到结果集包含的对象.取值操作可以通过迭代,切片,或其它专门的函数来实现.

一个结果集就是一个可迭代对象.

因此,可以通过一个循环来取出它的值:

for p in Poll.objects.all():
print p

将使用 Poll 对象的 repr() 方法打印出所有的 Poll 对象.

一个结果集也可以被切片, 使用数组符号操作:
fifth_poll = Poll.objects.all()[4]
all_polls_but_the_first_two = Poll.objects.all()[2:]
every_second_poll = Poll.objects.all()[::2]
结果集对象是惰性对象 - 也就是说,他们不是 真正的 包含他们表示对象的集合 (或列表).
Python 的协议魔法让结果集看起来是一个可迭代,可切片的对象. 事实上在幕后, Django 使用了缓存技术..

如果你真的需要一个列表, 你可以强制对一个惰性对象取值:

querylist = list(Poll.objects.all())

不过,最好不要这么做,尤其当一个结果集相当大时. 由于 Django 要创建每一个对象的内存表示,这将占用相当大的内存.

结果集及其缓存行为

每个结果集都包含一个 cache. 对一个新创建的结果集来说, 缓存区是空的.当一个结果集第一次被取值, Django 会进行一次数据库查询,并将查询结果放入缓存中, 之后返回用户需要的数据. 后面的取值操作会使用缓存中的数据而不用再次访问数据库.

必须时刻记住:结果集具有缓存行为. 下面两行语句生成了两个临时的结果集,并进行了取值,之后舍弃:

print [p for p in Poll.objects.all()] # Evaluate the Query Set
print [p for p in Poll.objects.all()] # Evaluate the Query Set again

对一个小型的,低流量的站点来说,这不会造成严重问题.
不过,对一个高访问量的站点来说,它双倍增加了数据库服务器的负担.
另外,由于在两次操作之间可能有其它的用户增加或删除了投票,因此这两次操作得到结果可能并不相同.

要避免这个问题, 保存这个结果集并在后面重用该结果集:
queryset = Poll.objects.all()
print [p for p in queryset] # Evaluate the query set
print [p for p in queryset] # Re-use the cache from the evaluation

关系 (连接)

当你在 model 中定义了一个关系字段(也就是,一个ForeignKey, OneToOneField, 或 ManyToManyField).
Django 使用关系字段的名字为 model 的每个实例添加一个 描述符.
在访问对象或关联对象时, 这个描述符就象一个常规属性.
举例来说, mychoice.poll 会返回 Choice 实例对象关联的 Poll 对象.

通过下面的关系,连接可以以非显式的方式进行:
choices.objects.filter(poll__slug="eggs")
得到一个 Choice 对象列表, 这些对象关联的 Poll 对象的 slug 字段值为 eggs. 允许多级连接.

通过一个对象实例的便利函数(convenience functions)就可直接查询该对象的关联对象. 举例来说, 如果 p 是一个 Poll 实例, p.choice_set() 将返回所有关联的 Choice 对象列表. 聪明的读者会注意到它等价于 choices.objects.filter(poll__id=p.id), 只是更加清晰.

One-to-one relations

one-to-one 关系中的每个对象拥有一个 get_relatedobjectname() 方法.

举例来说:

class Place(meta.Model):
# ...

class Restaurant(meta.Model):
# ...
    the_place = meta.OneToOneField(places.Place)

在上面的例子里, 每个 Place 会自动拥有一个 get_restaurant() 方法,
且每个 Restaurant 会自动拥有一个 get_the_place() 方法.

Many-to-one relations

在 many-to-one 关系中, 关联对象(Many)会自动拥有一个 get_relatedobject() 方法.
被关联的对象(one)会自动拥有 get_relatedobject(), get_relatedobject_list(), 和 get_relatedobject_count() 方法 (功能与模块级的 get_object(), filter(), 和 get_count() 相同).

在上面的民意测试例子里, 一个 Poll 对象 p 自动拥有下列方法:

p.get_choice()
p.get_choice_list()
p.get_choice_count()

Choice 对象 c 则自动拥有下面的方法: c.get_poll()

Many-to-many 关系

Many-to-many 关系类似Many-to-one relations_, 它生成同样的方法集.例外的是关联对象的 get_relatedobject_list() 方法返回一个实例的列表而不是一个仅一个实例.因此,若 Poll 和 Choice 是 many-to-many 关系, choice.get_poll_list() 将返回一个列表.

专门的结果集

除 filter 和 exclude() 之外, Django 提供了一系列结果集处理方法, 修改结果的类型, 或修改 sql 查询在数据库执行的方式.

order_by(*fields)

根据 model 中提供 ordering tuple, 结果集会被自动排序. 不过, 排序也可以通过 order_by 方法显式的进行:

Poll.objects.filter(pub_date__year=2005,
pub_date__month=1).order_by('-pub_date', 'question')

结果集将按降序排列 pub_date, 然后按升序排列 question."-pub_date" 中的负号表示降序(递减).要取随机序,使用"?", 象下面这样:

Poll.objects.order_by=('?')

要按另一个表中的字段排序, 添加另一个表的名字和一个句点,象下面这样:

Choice.objects.order_by=('Poll.pub_date', 'choice')

values(*fields)

类似 filter(), 不过它返回一个字典的列表而不是 model 实例对象的列表.

它接受一个可选参数: fields, 这是一个字段名列表或tuple.如果你没有指定 fields, 每个字段都会返回.
否则就只返回你指定的字段名和值.这里有一个例子,使用上面定义的 Poll model

>>> from datetime import datetime
>>> p1 = Poll(slug='whatsup', question="What's up?",
... pub_date=datetime(2005, 2, 20), expire_date=datetime(2005, 3, 20))
>>> p1.save()
>>> p2 = Poll(slug='name', question="What's your name?",
... pub_date=datetime(2005, 3, 20), expire_date=datetime(2005, 4, 20))
>>> p2.save()
>>> Poll.objects.all()
[What's up?, What's your name?]
>>> Poll.objects.values()
[{'id': 1, 'slug': 'whatsup', 'question': "What's up?", 'pub_date': datetime.datetime(2005, 2, 20), 'expire_date': datetime.datetime(2005, 3, 20)},
{'id': 2, 'slug': 'name', 'question': "What's your name?", 'pub_date': datetime.datetime(2005, 3, 20), 'expire_date': datetime.datetime(2005, 4, 20)}]
>>> Poll.objects.values(fields=['id', 'slug'])
[{'id': 1, 'slug': 'whatsup'}, {'id': 2, 'slug': 'name'}]

当你知道你要取得哪些字段的值时并且你不需要那些 model实例对象的功能时,使用 values() 函数.

Clone this wiki locally