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