django python query Making queries -- 模型的数据库查询 good

Making queries -- 模型的数据库查询

参考

  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

你可能感兴趣的:(python)