参考
Making queries
Creating objects
Saving changes to objects
Saving ForeignKey and ManyToManyField fields
Retrieving objects
Retrieving all objects
Retrieving specific objects with filters
Field lookups
Lookups that span relationships
Filters can reference fields on the model
The pk lookup shortcut
Escaping percent signs and underscores in LIKE statements
Caching and QuerySets
Complex lookups with Q objects
Comparing objects
Deleting objects
Updating multiple objects at once
Related objects
One-to-many relationships
One-to-One relationships

参考

  1. http://docs.djangoproject.com/en/dev/topics/db/queries/#topics-db-queries

Making queries

一当创建好一个数据模型 (data models),Django 默认提供了一套数据库抽象的 API,可以 create,retrieve,update 和 delete 对象。本文描述如何使用这些 API。

下面定义的 models 是我们的示例中要使用的:

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 使用一种非常直观的方式来表现数据库表:一个 model class 表示一个 数据库表,一个 class 的实例表示数据库表中的一条记录。

创建一个 object , 使用参数实例化一个 model class ,然后调用 save() 方法 就可以将数据保存到数据库里。

你可以从任何 Python 能找到的地方 import 一个 model class :

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

上面的实例化和save()相当于执行了一条 INSERT SQL 语句。只有你明确地调用 save() 方法,不如 Django 不会动你的数据库。 save() 没有返回数据。

可以调用 create() ,一次性执行创建实例和 save() 的过程。

Saving changes to objects

将改变保存到数据库也用 save() 方法。例如:

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

上面的相当于执行一条 UPDATE SQL 语句。

Saving ForeignKey and ManyToManyField fields

保存 ForeignKey field 和保存其他数据完全一致:

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

上面的 cheese_blog 是一个 Blog 实例对象, entry 是一个 Entry 实例对象。 在开始部分的 model 定义里, Entry 有一个 blog 属性,是一个 ForeignKey 。这里直接把 cheese_blog 赋给 entry.blog 了,后面会看到还有其他方式。

更新一个 ManyToManyField 有点不同,要使用 add() 方法:

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

Retrieving objects

要想从数据库里查询数据,需要用 Manager 在 model class 上创建一个 QuerySet。

QuerySet 是从数据库得到一个对象集合,可以是零、壹或者多个 filters 。在 SQL 语法里, QuerySet 等价于一条 SELECT 语句, 一个 filter 类似于 WHERE 或者 LIMIT 。

我们是通过 model 的 Manager 获取 QuerySet 的,每个 model 至少需要一个 Manager ,默认的名字是 "objects" (我们可以像定义其他 Class 属性一样, 将一个 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."

Retrieving all objects

得到一个数据库表的所有记录对象很简单:

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

Retrieving specific objects with filters

Manager 提供的 root QuerySet 描述了数据库表里的所有 objects。但更多时候 我们想知道某些特定的数据。我们需要对这个 root QuerySet 做进一步工作。

最常用的两种方式:

filter(**kwargs)
返回一个符合指定条件的新的 QuerySet
exclude(**kwargs)
返回不符合指定条件的新的 QuerySet

例如,获取 2006 年的 entries :

Entry.objects.filter(pub_date__year=2006)

此处我们不需要使用 all() ,虽然 Entry.objects.all().filter(...) 也能工 作。只有在想取得所有 objects 时候,需要 all()

CHAINING FILTERS (连接多个 filter)

查询的结果也是 QuerySet ,所以我们可以很容易连接多个查询:

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

FILTERED QUERYSETS ARE UNIQUE

如果想得到每次查询的结果,可以保存起来,这样可以作为下次查询的 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 ARE LAZY

OTHER QUERYSET METHODS

大多数情况下只需要 all(),filter() 和 exclude() ,如果想知道更多的 QuerySet 方法,参考: QuerySet API

Limiting QuerySets

可以使用 Python 的 list 切片分割 QuerySet :

>>> Entry.objects.all()[:5]
>>> Entry.objects.all()[5:10]
>>> Entry.objects.all()[:10:2]
>>> Entry.objects.order_by('headline')[0]
>>> Entry.objects.order_by('headline')[0:1].get()

Field lookups

最常见的 lookups 是 "field__lookuptype=value" :

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

相当于下面 SQL 语句 :

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

exact

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

相当于:

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

默认使用 exact ,下面两条语句等价:

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

iexact

忽略大小写的绝对查询:

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

对于 "Beatles Blog", "beatles blog", "BeAtlES blOG" 等都能匹配。

contains / icontains

contains 大小写敏感的相似查询:

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

相当于 SQL 语句:

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

startswith / endswith

Lookups that span relationships

扩展关系查询。例如,查询所有 Entry 对象里面 Blog 属性名字为 'Beatles Blog' :

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

对于所有 Blog 对象里,至少有一个 Entry 属性的 headline 包含 'Lennon' 查 询:

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

这种关系查询方式下,如果对应的对象没有某个属性,也并不和其他查询一样返 回错误。

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

Spanning multi-valued relationships

如果想 filtering 一个 ManyToManyField 或反向 ForeignKeyField, 有两种不 同方法。

思考示例里的 Blog/Entry 关系(Blog 到 Entry 是 one-to-many 关系)。思考 下面示例:

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

Filters can reference fields on the model

要想把要查询的数据和 model 自己的字段对比,即使用 model 自己字段作为一 个查询量,可以使用 F 方法:

>>> from django.db.models import F
>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments'))
>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2)
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

这里 n_pingbacks 和 n_comments 都时 Entry model 的属性。

甚至可以在关系中使用:

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

The pk lookup shortcut

pk 表示 "primary key" , Django 默认为每个 model 创建一个 id 属性(自增 的 primary key),可以使用 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)

在关系连接中也可以使用:

>>> 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

Escaping percent signs and underscores in LIKE statements

使用 SQL 的 LIKE 语句查询的 field lookups (iexact, contains, icontains, startswith, istartswith, endswith and iendswith) 会自动替换 两个特殊字符: %(百分号) 和 (下划线) 。在 LIKE 语句中百分号是元字符, 下划线也是。

对于这样的 Django 查询:

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

对应这样语句:

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

Caching and QuerySets

Complex lookups with Q objects

在 filter 里面的查询关系都是 'AND' 逻辑。如果想表达复杂的查询,请使用 Q 方法(django.db.models.Q)。 Q 方法会封装一些查询,简单封装示例如下:

Q(question__startswith='What')

Q 方法可以使用 "&" 和 "|" 符号。

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

相当于:

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

还可以使用 "~" 表示 "NOT" :

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

每个 lookup 函数 (如 filter(), exclude(), get())都可以使用一个和多个 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')

Lookup 函数也可以混合使用 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)))

Comparing objects

同 Python 其他对象一样:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name

Deleting objects

删除一个对象很简单:

e.delete()

每个 QuerySet 都有 delete() 方法,可以批量删除:

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

Updating multiple objects at once

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

update() 方法只能在没有外联( non-relation )或者 ForeignKey fields 上使用:

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

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

update() 会立即应用并返回修改的对象个数。唯一的限制是,必须作用于 main table :

>>> 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')

也可以使用 F() :

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

但是不能使用关联的属性:

# 下面语句会引发 FieldError 异常
>>> Entry.objects.update(headline=F('blog__name'))

Related objects

如果在 model 里定义了 relationship (比如:ForeignKey, OneToOneField, or ManyToManyField)。这个 model 的示例也有取得其关系对象的方法。

再次重申,下面所有示例使用本文开头定义的 model 。

Blog 对象 b 要访问关联的 Entry 对象,可以通过 entry_set 属性:b.entry_set.all()

One-to-many relationships

FORWARD (正向访问)

如果一个 model 有一个 ForeignKey ,通过它的实例就可以访问这个关联对象:

>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.

可以访问和设置这个 foreignkey 属性:

>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()

如果 Foreignkey 的定义有 "null=True" 设置,可以把 "None" 赋给它:

>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"

对于 cache:

>>> e = Entry.objects.get(id=2)
>>> print e.blog  # Hits the database to retrieve the associated Blog.
>>> print e.blog  # Doesn't hit the database; uses cached version.

>>> e = Entry.objects.select_related().get(id=2)
>>> print e.blog  # Doesn't hit the database; uses cached version.
>>> print e.blog  # Doesn't hit the database; uses cached version.

FOLLOWING RELATIONSHIPS "BACKWARD" (反向)

如果一个 model A 有 Foreignkey, 那么 A 的 Foreignkey 指向的 model B 的 实例便有一个 Manager 方法。这个 Manager 方法可以返回所有关联的 model A 的实例。

默认情况下这个 Manager 名称形式为: "FOO_set" 。 "FOO" 是 model A 的 Foreignkey field 属性名。 这个 Manager 返回的也是 QuerySet 形式。

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

注意: "FOO_set" 字符串也可以替换为 Foreignkey field 定义时用 related_name 指定的字符串。比如我们的 Entry model 里有这样的定义:"blog = Foreignkey(Blog, related_name='entries'), 我们就可以用下面形式:

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()

ForeignKey 的 QuerySet 和上面介绍的 QuerySet 比,还有下面方法:

add(obj1,obj2,...)
增加指定的 model objects 到 related object set。
create(kwargs)
创建行 object ,保存并把它放到 related object set 里。返回新创建的 object。
remove(obj1,obj2,...)
从 related object set 里删除
clear()
清楚所有 related object set

还可以这样:

b = Blog.objects.get(id=1)
b.entry_set = [e1, e2]
Many-to-many relationships

和 one-to-many relationship 的 "backward" 方法一致,唯一区别就是属性的 名字: 定义 ManyToManyField 的 model 使用 field 自身的名字访问,而 "reverse" model (reverse model 指 ManyToManyField 里指向的 model) 使用 小写 model name 加上 'set' 。

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name_contains='John')

a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

ManyToManyField 也可以使用 related_name 指定的字符串,假如 Entry 的定义 "authors = models.ManyToManyField(Author)" 修改为 'authors = models.ManyToManyField(Author,related_name="entries")' ,那么 Author 实 例就可以用 'entries' 代替 'entry_set' 来访问了。

One-to-One relationships

和 many-to-one 很相似 :

class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

不同的地方是 "reverse" 查询:

e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

How are the backward relationships possible?

Queries over related objects

Entry.objects.filter(blog=b) # Query using object instance
Entry.objects.filter(blog=b.id) # Query using id from instance
Entry.objects.filter(blog=5) # Query using id directly

Falling back to raw SQL

想使用 raw SQL : Performing raw SQL queries