在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 執(zhí)行查詢(xún)
編寫(xiě)自定義存儲(chǔ)系統(tǒng)
高級(jí)
編寫(xiě)你的第一個(gè)Django應(yīng)用,第5部分
視圖層
Django管理文檔生成器
編寫(xiě)你的第一個(gè) Django 程序 第3部分
編寫(xiě)你的第一個(gè)Django應(yīng)用,第6部分
模型層
中間件
測(cè)試
數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)優(yōu)化
文件上傳
中間件
驗(yàn)證器
基于類(lèi)的內(nèi)建通用視圖
部署靜態(tài)文件
使用Django認(rèn)證系統(tǒng)
高級(jí)教程:如何編寫(xiě)可重用的應(yīng)用
Model 類(lèi)參考
Django 初探
使用Django輸出PDF
模型的實(shí)例
模型
文件上傳
進(jìn)行原始的sql查詢(xún)
面向程序員
中間件
編寫(xiě)數(shù)據(jù)庫(kù)遷移
TemplateResponse 和 SimpleTemplateResponse
異常
Django中的測(cè)試
基礎(chǔ)
管理器
File對(duì)象
URL調(diào)度器
加密簽名
國(guó)際化和本地化
日志
查詢(xún)集
django-admin 和 manage.py
使用基于類(lèi)的視圖處理表單
聚合
內(nèi)建基于類(lèi)的視圖的API
如何使用會(huì)話(huà)
部署 Django
其它
其它核心功能
高級(jí)
Django中的密碼管理
模型元選項(xiàng)
按需內(nèi)容處理
查找 API 參考
高級(jí)
Django 的快捷函數(shù)
Django 的設(shè)置
Admin
開(kāi)發(fā)過(guò)程
新手入門(mén)
基于類(lèi)的視圖
模型實(shí)例參考
信號(hào)
表單素材 ( <code>Media</code> 類(lèi))
自定義查找
常見(jiàn)的網(wǎng)站應(yīng)用工具
模型
django.contrib.humanize
Django模版語(yǔ)言
點(diǎn)擊劫持保護(hù)
管理操作
編寫(xiě)你的第一個(gè) Django 程序 第2部分
Django安全
模式編輯器
多數(shù)據(jù)庫(kù)
部署
基于類(lèi)的視圖
內(nèi)建的視圖
視圖裝飾器
面向設(shè)計(jì)師
編寫(xiě)視圖
應(yīng)用程序
如何使用WSGI 部署
參考
表單 API
文件儲(chǔ)存API
認(rèn)證
國(guó)際化和本地化
錯(cuò)誤報(bào)告
基礎(chǔ)
基礎(chǔ)
將遺留數(shù)據(jù)庫(kù)整合到Django
教程
Django異常
編寫(xiě)你的第一個(gè) Django 程序 第4部分
遷移
分頁(yè)
重定向應(yīng)用
表單
從零開(kāi)始
為模型提供初始數(shù)據(jù)
設(shè)置
使用Django輸出CSV
關(guān)聯(lián)對(duì)象參考
使用表單
Django 中的用戶(hù)認(rèn)證
快速安裝指南
安全問(wèn)題歸檔
數(shù)據(jù)庫(kù)函數(shù)
編寫(xiě)自定義的django-admin命令
高級(jí)教程
管理文件
格式本地化
基于類(lèi)的通用視圖 —— 索引
安全
系統(tǒng)檢查框架
為Django編寫(xiě)首個(gè)補(bǔ)丁
模板層
Widgets
編寫(xiě)你的第一個(gè) Django 程序 第1部分
執(zhí)行查詢(xún)

執(zhí)行查詢(xún)

一旦你建立好數(shù)據(jù)模型之后,django會(huì)自動(dòng)生成一套數(shù)據(jù)庫(kù)抽象的API,可以讓你執(zhí)行增刪改查的操作。這篇文檔闡述了如何使用這些API。關(guān)于所有模型檢索選項(xiàng)的詳細(xì)內(nèi)容,請(qǐng)見(jiàn)數(shù)據(jù)模型參考。

在整個(gè)文檔(以及參考)中,我們會(huì)大量使用下面的模型,它構(gòu)成了一個(gè)博客應(yīng)用。

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):              # __unicode__ on Python 2
        return self.headline

創(chuàng)建對(duì)象

為了把數(shù)據(jù)庫(kù)表中的數(shù)據(jù)表示成python對(duì)象,django使用一種直觀的方式:一個(gè)模型類(lèi)代表數(shù)據(jù)庫(kù)的一個(gè)表,一個(gè)模型的實(shí)例代表數(shù)據(jù)庫(kù)表中的一條特定的記錄。

使用關(guān)鍵詞參數(shù)實(shí)例化一個(gè)對(duì)象來(lái)創(chuàng)建它,然后調(diào)用save()把它保存到數(shù)據(jù)庫(kù)中。

假設(shè)模型存放于文件mysite/blog/models.py中,下面是一個(gè)例子:

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

上面的代碼在背后執(zhí)行了sql的INSERT操作。在你顯式調(diào)用save()之前,django不會(huì)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。

save()方法沒(méi)有返回值。

請(qǐng)參見(jiàn)

save()方法帶有一些高級(jí)選項(xiàng),它們沒(méi)有在這里給出,完整的細(xì)節(jié)請(qǐng)見(jiàn)save()文檔。

如果你想只用一條語(yǔ)句創(chuàng)建并保存一個(gè)對(duì)象,使用create()方法。

保存對(duì)象的改動(dòng)

調(diào)用save()方法,來(lái)保存已經(jīng)存在于數(shù)據(jù)庫(kù)中的對(duì)象的改動(dòng)。

假設(shè)一個(gè)Blog的實(shí)例b5已經(jīng)被保存在數(shù)據(jù)庫(kù)中,這個(gè)例子更改了它的名字,并且在數(shù)據(jù)庫(kù)中更新它的記錄:

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

上面的代碼在背后執(zhí)行了sql的UPDATE操作。在你顯式調(diào)用save()之前,django不會(huì)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。

保存ForeignKeyManyToManyField字段

更新ForeignKey字段的方式和保存普通字段相同--只是簡(jiǎn)單地把一個(gè)類(lèi)型正確的對(duì)象賦值到字段中。下面的例子更新了Entry類(lèi)的實(shí)例entryblog屬性,假設(shè)Entry的一個(gè)合適的實(shí)例以及Blog已經(jīng)保存在數(shù)據(jù)庫(kù)中(我們可以像下面那樣獲取他們):

>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

更新ManyToManyField的方式有一些不同--使用字段的add()方法來(lái)增加關(guān)系的記錄。這個(gè)例子向entry對(duì)象添加Author類(lèi)的實(shí)例joe

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

為了在一條語(yǔ)句中,向ManyToManyField添加多條記錄,可以在調(diào)用add()方法時(shí)傳入多個(gè)參數(shù),像這樣:

>>> 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將會(huì)在你添加錯(cuò)誤類(lèi)型的對(duì)象時(shí)拋出異常。

獲取對(duì)象

通過(guò)模型中的Manager構(gòu)造一個(gè)QuertSet,來(lái)從你的數(shù)據(jù)庫(kù)中獲取對(duì)象。

QuerySet表示你數(shù)據(jù)庫(kù)中取出來(lái)的一個(gè)對(duì)象的集合。它可以含有零個(gè)、一個(gè)或者多個(gè)過(guò)濾器,過(guò)濾器根據(jù)所給的參數(shù)限制查詢(xún)結(jié)果的范圍。在sql的角度,QuerySetSELECT命令等價(jià),過(guò)濾器是像WHERELIMIT一樣的限制子句。

你可以從模型的Manager那里取得QuerySet。每個(gè)模型都至少有一個(gè)Manager,它通常命名為objects。通過(guò)模型類(lèi)直接訪(fǎng)問(wèn)它,像這樣:

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

注意

管理器通常只可以通過(guò)模型類(lèi)來(lái)訪(fǎng)問(wèn),不可以通過(guò)模型實(shí)例來(lái)訪(fǎng)問(wèn)。這是為了強(qiáng)制區(qū)分表級(jí)別和記錄級(jí)別的操作。

對(duì)于一個(gè)模型來(lái)說(shuō),ManagerQuerySet的主要來(lái)源。例如, Blog.objects.all() 會(huì)返回持有數(shù)據(jù)庫(kù)中所有Blog對(duì)象的一個(gè)QuerySet。

獲取所有對(duì)象

獲取一個(gè)表中所有對(duì)象的最簡(jiǎn)單的方式是全部獲取。使用Managerall()方法:

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

all()方法返回包含數(shù)據(jù)庫(kù)中所有對(duì)象的QuerySet。

使用過(guò)濾器獲取特定對(duì)象

all()方法返回的結(jié)果集中包含全部對(duì)象,但是更普遍的情況是你需要獲取完整集合的一個(gè)子集。

要?jiǎng)?chuàng)建這樣一個(gè)子集,需要精煉上面的結(jié)果集,增加一些過(guò)濾器作為條件。兩個(gè)最普遍的途徑是:

filter(**kwargs) 返回一個(gè)包含對(duì)象的集合,它們滿(mǎn)足參數(shù)中所給的條件。

exclude(**kwargs) 返回一個(gè)包含對(duì)象的集合,它們滿(mǎn)足參數(shù)中所給的條件。

查詢(xún)參數(shù)(上面函數(shù)定義中的**kwargs)需要滿(mǎn)足特定的格式,字段檢索一節(jié)中會(huì)提到。

舉個(gè)例子,要獲取年份為2006的所有文章的結(jié)果集,可以這樣使用filter()方法:

Entry.objects.filter(pub_date__year=2006)

在默認(rèn)的管理器類(lèi)中,它相當(dāng)于:

Entry.objects.all().filter(pub_date__year=2006)

鏈?zhǔn)竭^(guò)濾

QuerySet的精煉結(jié)果還是QuerySet,所以你可以把精煉用的語(yǔ)句組合到一起,像這樣:

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

最開(kāi)始的QuerySet包含數(shù)據(jù)庫(kù)中的所有對(duì)象,之后增加一個(gè)過(guò)濾器去掉一部分,在之后又是另外一個(gè)過(guò)濾器。最后的結(jié)果的一個(gè)QuerySet,包含所有標(biāo)題以”word“開(kāi)頭的記錄,并且日期是2005年一月,日為當(dāng)天的值。

過(guò)濾后的結(jié)果集是獨(dú)立的

每次你篩選一個(gè)結(jié)果集,得到的都是全新的另一個(gè)結(jié)果集,它和之前的結(jié)果集之間沒(méi)有任何綁定關(guān)系。每次篩選都會(huì)創(chuàng)建一個(gè)獨(dú)立的結(jié)果集,可以被存儲(chǔ)及反復(fù)使用。

例如:

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

這三個(gè) QuerySets 是不同的。 第一個(gè) QuerySet 包含大標(biāo)題以"What"開(kāi)頭的所有記錄。第二個(gè)則是第一個(gè)的子集,用一個(gè)附加的條件排除了出版日期 pub_date 是今天的記錄。 第三個(gè)也是第一個(gè)的子集,它只保留出版日期 pub_date 是今天的記錄。 最初的 QuerySet (q1) 沒(méi)有受到篩選的影響。

查詢(xún)集是延遲的

QuerySets 是惰性的 -- 創(chuàng)建 QuerySet 的動(dòng)作不涉及任何數(shù)據(jù)庫(kù)操作。你可以一直添加過(guò)濾器,在這個(gè)過(guò)程中,Django 不會(huì)執(zhí)行任何數(shù)據(jù)庫(kù)查詢(xún),除非 QuerySet 被執(zhí)行. 看看下面這個(gè)例子:

>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.now())
>>> q = q.exclude(body_text__icontains="food")
>>> print q

雖然上面的代碼看上去象是三個(gè)數(shù)據(jù)庫(kù)操作,但實(shí)際上只在最后一行 (print q) 執(zhí)行了一次數(shù)據(jù)庫(kù)操作,。一般情況下, QuerySet 不能從數(shù)據(jù)庫(kù)中主動(dòng)地獲得數(shù)據(jù),得被動(dòng)地由你來(lái)請(qǐng)求。對(duì) QuerySet 求值就意味著 Django 會(huì)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。想了解對(duì)查詢(xún)集何時(shí)求值,請(qǐng)查看 何時(shí)對(duì)查詢(xún)集求值 (When QuerySets are evaluated).

其他查詢(xún)集方法

大多數(shù)情況使用 all(), filter() 和 exclude() 就足夠了。 但也有一些不常用的;請(qǐng)查看 查詢(xún)API參考 (QuerySet API Reference) 中完整的 QuerySet 方法列表。

限制查詢(xún)集范圍

可以用 python 的數(shù)組切片語(yǔ)法來(lái)限制你的 QuerySet 以得到一部分結(jié)果。它等價(jià)于SQL中的 LIMIT 和 OFFSET 。

例如,下面的這個(gè)例子返回前五個(gè)對(duì)象 (LIMIT 5):

>>> Entry.objects.all()[:5]

這個(gè)例子返回第六到第十之間的對(duì)象 (OFFSET 5 LIMIT 5):

>>> Entry.objects.all()[5:10]

Django 不支持對(duì)查詢(xún)集做負(fù)數(shù)索引 (例如 Entry.objects.all()[-1]) 。

一般來(lái)說(shuō),對(duì) QuerySet 切片會(huì)返回新的 QuerySet -- 這個(gè)過(guò)程中不會(huì)對(duì)運(yùn)行查詢(xún)。不過(guò)也有例外,如果你在切片時(shí)使用了 "step" 參數(shù),查詢(xún)集就會(huì)被求值,就在數(shù)據(jù)庫(kù)中運(yùn)行查詢(xún)。舉個(gè)例子,使用下面這個(gè)這個(gè)查詢(xún)集返回前十個(gè)對(duì)象中的偶數(shù)次對(duì)象,就會(huì)運(yùn)行數(shù)據(jù)庫(kù)查詢(xún):

>>> Entry.objects.all()[:10:2]

要檢索單獨(dú)的對(duì)象,而非列表 (比如 SELECT foo FROM bar LIMIT 1),可以直接使用索引來(lái)代替切片。舉個(gè)例子,下面這段代碼將返回大標(biāo)題排序后的第一條記錄 Entry:

>>> Entry.objects.order_by('headline')[0]

大約等價(jià)于:

>>> Entry.objects.order_by('headline')[0:1].get()

要注意的是:如果找不到符合條件的對(duì)象,第一種方法會(huì)拋出 IndexError ,而第二種方法會(huì)拋出 DoesNotExist。 詳看 get() 。

字段篩選條件

字段篩選條件就是 SQL 語(yǔ)句中的 WHERE 從句。就是 Django 中的 QuerySet 的 filter(), exclude() 和 get() 方法中的關(guān)鍵字參數(shù)。

篩選條件的形式是 field__lookuptype=value 。 (注意:這里是雙下劃線(xiàn))。例如:

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

大體可以翻譯為如下的 SQL 語(yǔ)句:

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

這是怎么辦到的?

Python 允許函式接受任意多 name-value 形式的參數(shù),并在運(yùn)行時(shí)才確定name和value的值。詳情請(qǐng)參閱官方Python教程中的 關(guān)鍵字參數(shù)(Keyword Arguments)。

如果你傳遞了一個(gè)無(wú)效的關(guān)鍵字參數(shù),會(huì)拋出 TypeError 導(dǎo)常。

數(shù)據(jù)庫(kù) API 支持24種查詢(xún)類(lèi)型;可以在 字段篩選參考(field lookup reference) 查看詳細(xì)的列表。為了給您一個(gè)直觀的認(rèn)識(shí),這里我們列出一些常用的查詢(xún)類(lèi)型:

exact

"exact" 匹配。例如:

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

會(huì)生成如下的 SQL 語(yǔ)句:

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

如果你沒(méi)有提供查詢(xún)類(lèi)型 -- 也就是說(shuō)關(guān)鍵字參數(shù)中沒(méi)有雙下劃線(xiàn),那么查詢(xún)類(lèi)型就會(huì)被指定為 exact。

舉個(gè)例子,這兩個(gè)語(yǔ)句是相等的:

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

這樣做很方便,因?yàn)?exact 是最常用的。

iexact

忽略大小寫(xiě)的匹配。所以下面的這個(gè)查詢(xún):

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

會(huì)匹配標(biāo)題是 "Beatles Blog", "beatles blog", 甚至 "BeAtlES blOG" 的 Blog

contains

大小寫(xiě)敏感的模糊匹配。 例如:

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

大體可以翻譯為如下的 SQL:

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

要注意這段代碼匹配大標(biāo)題 'Today Lennon honored' ,而不能匹配 'today lennon honored'。

它也有一個(gè)忽略大小寫(xiě)的版本,就是 icontains。

startswith, endswith

分別匹配開(kāi)頭和結(jié)尾,同樣也有忽略大小寫(xiě)的版本 istartswith 和 iendswith。 再?gòu)?qiáng)調(diào)一次,這僅僅是簡(jiǎn)短介紹。完整的參考請(qǐng)參見(jiàn) 字段篩選條件參考(field lookup reference)。

跨關(guān)系查詢(xún)

Django 提供了一種直觀而高效的方式在查詢(xún)(lookups)中表示關(guān)聯(lián)關(guān)系,它能自動(dòng)確認(rèn) SQL JOIN 聯(lián)系。要做跨關(guān)系查詢(xún),就使用兩個(gè)下劃線(xiàn)來(lái)鏈接模型(model)間關(guān)聯(lián)字段的名稱(chēng),直到最終鏈接到你想要的 model 為止。

這個(gè)例子檢索所有關(guān)聯(lián) Blog 的 name 值為 'Beatles Blog' 的所有 Entry 對(duì)象:

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

跨關(guān)系的篩選條件可以一直延展。

關(guān)系也是可逆的。可以在目標(biāo) model 上使用源 model 名稱(chēng)的小寫(xiě)形式得到反向關(guān)聯(lián)。

下面這個(gè)例子檢索至少關(guān)聯(lián)一個(gè) Entry 且大標(biāo)題 headline 包含 'Lennon' 的所有 Blog 對(duì)象:

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

如果在某個(gè)關(guān)聯(lián) model 中找不到符合過(guò)濾條件的對(duì)象,Django 將視它為一個(gè)空的 (所有的值都是 NULL), 但是可用的對(duì)象。這意味著不會(huì)有異常拋出,在這個(gè)例子中:

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

(假設(shè)關(guān)聯(lián)到 Author 類(lèi)), 如果沒(méi)有哪個(gè) author 與 entry 相關(guān)聯(lián),Django 會(huì)認(rèn)為它沒(méi)有 name 屬性,而不會(huì)因?yàn)椴淮嬖?author 拋出異常。通常來(lái)說(shuō),這正是你所希望的機(jī)制。唯一的例外是使用 isnull 的情況。如下:

Blog.objects.filter(entry__author__name__isnull=True)

這段代碼會(huì)得到 author 的 name 為空的 Blog 或 entry 的 author為空的 Blog。 如果不嫌麻煩,可以這樣寫(xiě):

Blog.objects.filter (entry__author__isnull=False,
        entry__author__name__isnull=True)

跨一對(duì)多/多對(duì)多關(guān)系(Spanning multi-valued relationships)

這部分是Django 1.0中新增的: 請(qǐng)查看版本記錄 如果你的過(guò)濾是基于 ManyToManyField 或是逆向 ForeignKeyField 的,你可能會(huì)對(duì)下面這兩種情況感興趣。回顧 Blog/Entry 的關(guān)系(Blog 到 Entry 是一對(duì)多關(guān)系),如果要查找這樣的 blog:它關(guān)聯(lián)一個(gè)大標(biāo)題包含"Lennon",且在2008年出版的 entry ;或者要查找這樣的 blogs:它關(guān)聯(lián)一個(gè)大標(biāo)題包含"Lennon"的 entry ,同時(shí)它又關(guān)聯(lián)另外一個(gè)在2008年出版的 entry 。因?yàn)橐粋€(gè) Blog 會(huì)關(guān)聯(lián)多個(gè)的Entry,所以上述兩種情況在現(xiàn)實(shí)應(yīng)用中是很有可能出現(xiàn)的。

同樣的情形也出現(xiàn)在 ManyToManyField 上。例如,如果 Entry 有一個(gè) ManyToManyField 字段,名字是 tags,我們想得到 tags 是"music"和"bands"的 entries,或者我們想得到包含名為"music" 的標(biāo)簽而狀態(tài)是"public"的 entry。

針對(duì)這兩種情況,Django 用一種很方便的方式來(lái)使用 filter() 和 exclude()。對(duì)于包含在同一個(gè) filter() 中的篩選條件,查詢(xún)集要同時(shí)滿(mǎn)足所有篩選條件。而對(duì)于連續(xù)的 filter() ,查詢(xún)集的范圍是依次限定的。但對(duì)于跨一對(duì)多/多對(duì)多關(guān)系查詢(xún)來(lái)說(shuō),在第二種情況下,篩選條件針對(duì)的是主 model 所有的關(guān)聯(lián)對(duì)象,而不是被前面的 filter() 過(guò)濾后的關(guān)聯(lián)對(duì)象。

這聽(tīng)起來(lái)會(huì)讓人迷糊,舉個(gè)例子會(huì)講得更清楚。要檢索這樣的 blog:它要關(guān)系一個(gè)大標(biāo)題中含有 "Lennon" 并且在2008年出版的 entry (這個(gè) entry 同時(shí)滿(mǎn)足這兩個(gè)條件),可以這樣寫(xiě):

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

要檢索另外一種 blog:它關(guān)聯(lián)一個(gè)大標(biāo)題含有"Lennon"的 entry ,又關(guān)聯(lián)一個(gè)在2008年出版的 entry (一個(gè) entry 的大標(biāo)題含有 Lennon,同一個(gè)或另一個(gè) entry 是在2008年出版的)??梢赃@樣寫(xiě):

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

在第二個(gè)例子中,第一個(gè)過(guò)濾器(filter)先檢索與符合條件的 entry 的相關(guān)聯(lián)的所有 blogs。第二個(gè)過(guò)濾器在此基礎(chǔ)上從這些 blogs 中檢索與第二種 entry 也相關(guān)聯(lián)的 blog。第二個(gè)過(guò)濾器選擇的 entry 可能與第一個(gè)過(guò)濾器所選擇的完全相同,也可能不同。 因?yàn)檫^(guò)濾項(xiàng)過(guò)濾的是 Blog,而不是 Entry。

上述原則同樣適用于 exclude():一個(gè)單獨(dú) exclude() 中的所有篩選條件都是作用于同一個(gè)實(shí)例 (如果這些條件都是針對(duì)同一個(gè)一對(duì)多/多對(duì)多的關(guān)系)。連續(xù)的 filter() 或 exclude() 卻根據(jù)同樣的篩選條件,作用于不同的關(guān)聯(lián)對(duì)象。

在過(guò)濾器中引用 model 中的字段(Filters can reference fields on the model)

這部分是 Django 1.1 新增的: 請(qǐng)查看版本記錄 在上面所有的例子中,我們構(gòu)造的過(guò)濾器都只是將字段值與某個(gè)常量做比較。如果我們要對(duì)兩個(gè)字段的值做比較,那該怎么做呢?

Django 提供 F() 來(lái)做這樣的比較。F() 的實(shí)例可以在查詢(xún)中引用字段,來(lái)比較同一個(gè) model 實(shí)例中兩個(gè)不同字段的值。

例如:要查詢(xún)回復(fù)數(shù)(comments)大于廣播數(shù)(pingbacks)的博文(blog entries),可以構(gòu)造一個(gè) F() 對(duì)象在查詢(xún)中引用評(píng)論數(shù)量:

>>> from django.db.models import F
>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments'))

Django 支持 F() 對(duì)象之間以及 F() 對(duì)象和常數(shù)之間的加減乘除和取模的操作。例如,要找到廣播數(shù)等于評(píng)論數(shù)兩倍的博文,可以這樣修改查詢(xún)語(yǔ)句:

>>> Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2)

要查找閱讀數(shù)量小于評(píng)論數(shù)與廣播數(shù)之和的博文,查詢(xún)?nèi)缦?

>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

你也可以在 F() 對(duì)象中使用兩個(gè)下劃線(xiàn)做跨關(guān)系查詢(xún)。F() 對(duì)象使用兩個(gè)下劃線(xiàn)引入必要的關(guān)聯(lián)對(duì)象。例如,要查詢(xún)博客(blog)名稱(chēng)與作者(author)名稱(chēng)相同的博文(entry),查詢(xún)就可以這樣寫(xiě):

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

主鍵查詢(xún)的簡(jiǎn)捷方式

為使用方便考慮,Django 用 pk 代表主鍵"primary key"。

以 Blog 為例, 主鍵是 id 字段,所以下面三個(gè)語(yǔ)句都是等價(jià)的:

>>> 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 對(duì) __exact 查詢(xún)同樣有效,任何查詢(xún)項(xiàng)都可以用 pk 來(lái)構(gòu)造基于主鍵的查詢(xún):

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

pk 查詢(xún)也可以跨關(guān)系,下面三個(gè)語(yǔ)句是等價(jià)的:

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

在LIKE語(yǔ)句中轉(zhuǎn)義百分號(hào)%和下劃線(xiàn)_

字段篩選條件相當(dāng)于 LIKE SQL 語(yǔ)句 (iexact, contains, icontains, startswith, istartswith, endswith 和 iendswith) ,它會(huì)自動(dòng)轉(zhuǎn)義兩個(gè)特殊符號(hào) -- 百分號(hào)%和下劃線(xiàn)_。(在 LIKE 語(yǔ)句中,百分號(hào)%表示多字符匹配,而下劃線(xiàn)_表示單字符匹配。)

這就意味著我們可以直接使用這兩個(gè)字符,而不用考慮他們的 SQL 語(yǔ)義。例如,要查詢(xún)大標(biāo)題中含有一個(gè)百分號(hào)%的 entry:

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

Django 會(huì)處理轉(zhuǎn)義;最終的 SQL 看起來(lái)會(huì)是這樣:

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

下劃線(xiàn)_和百分號(hào)%的處理方式相同,Django 都會(huì)自動(dòng)轉(zhuǎn)義。

緩存和查詢(xún)

每個(gè) QuerySet 都包含一個(gè)緩存,以減少對(duì)數(shù)據(jù)庫(kù)的訪(fǎng)問(wèn)。要編寫(xiě)高效代碼,就要理解緩存是如何工作的。

一個(gè) QuerySet 時(shí)剛剛創(chuàng)建的時(shí)候,緩存是空的。 QuerySet 第一次運(yùn)行時(shí),會(huì)執(zhí)行數(shù)據(jù)庫(kù)查詢(xún),接下來(lái) Django 就在 QuerySet 的緩存中保存查詢(xún)的結(jié)果,并根據(jù)請(qǐng)求返回這些結(jié)果(比如,后面再次調(diào)用這個(gè) QuerySet 的時(shí)候)。再次運(yùn)行 QuerySet 時(shí)就會(huì)重用這些緩存結(jié)果。

要牢住上面所說(shuō)的緩存行為,否則在使用 QuerySet 時(shí)可能會(huì)給你造成不小的麻煩。例如,創(chuàng)建下面兩個(gè) QuerySet ,并對(duì)它們求值,然后釋放:

>>> print [e.headline for e in Entry.objects.all()]
>>> print [e.pub_date for e in Entry.objects.all()]

這就意味著相同的數(shù)據(jù)庫(kù)查詢(xún)將執(zhí)行兩次,事實(shí)上讀取了兩次數(shù)據(jù)庫(kù)。而且,這兩次讀出來(lái)的列表可能并不完全相同,因?yàn)榇嬖谶@種可能:在兩次讀取之間,某個(gè) Entry 被添加到數(shù)據(jù)庫(kù)中,或是被刪除了。

要避免這個(gè)問(wèn)題,只要簡(jiǎn)單地保存 QuerySet 然后重用即可:

>>> queryset = Poll.objects.all()
>>> print [p.headline for p in queryset] # Evaluate the query set.
>>> print [p.pub_date for p in queryset] # Re-use the cache from the evaluation.

用 Q 對(duì)象實(shí)現(xiàn)復(fù)雜查找 (Complex lookups with Q objects)

在 filter() 等函式中關(guān)鍵字參數(shù)彼此之間都是 "AND" 關(guān)系。如果你要執(zhí)行更復(fù)雜的查詢(xún)(比如,實(shí)現(xiàn)篩選條件的 OR 關(guān)系),可以使用 Q 對(duì)象。

Q 對(duì)象(django.db.models.Q)是用來(lái)封裝一組查詢(xún)關(guān)鍵字的對(duì)象。這里提到的查詢(xún)關(guān)鍵字請(qǐng)查看上面的 "Field lookups"。

例如,下面這個(gè) Q 對(duì)象封裝了一個(gè)單獨(dú)的 LIKE 查詢(xún):

Q(question__startswith='What')

Q 對(duì)象可以用 & 和 | 運(yùn)算符進(jìn)行連接。當(dāng)某個(gè)操作連接兩個(gè) Q 對(duì)象時(shí),就會(huì)產(chǎn)生一個(gè)新的等價(jià)的 Q 對(duì)象。

例如,下面這段語(yǔ)句就產(chǎn)生了一個(gè) Q ,這是用 "OR" 關(guān)系連接的兩個(gè) "question__startswith" 查詢(xún):

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

上面的例子等價(jià)于下面的 SQL WHERE 從句:

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

你可以用 & 和 | 連接任意多的 Q 對(duì)象,而且可以用括號(hào)分組。Q 對(duì)象也可以用 ~ 操作取反,而且普通查詢(xún)和取反查詢(xún)(NOT)可以連接在一起使用:

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

每種查詢(xún)函式(比如 filter(), exclude(), get()) 除了能接收關(guān)鍵字參數(shù)以外,也能以位置參數(shù)的形式接受一個(gè)或多個(gè) Q 對(duì)象。如果你給查詢(xún)函式傳遞了多個(gè) Q 對(duì)象,那么它們彼此間都是 "AND" 關(guān)系。例如:

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

查找函式可以混用 Q 對(duì)象和關(guān)鍵字參數(shù)。查詢(xún)函式的所有參數(shù)(Q 關(guān)系和關(guān)鍵字參數(shù)) 都是 "AND" 關(guān)系。但是,如果參數(shù)中有 Q 對(duì)象,它必須排在所有的關(guān)鍵字參數(shù)之前。例如:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who')

... 是一個(gè)有效的查詢(xún)。但下面這個(gè)查詢(xún)雖然看上去和前者等價(jià):

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

... 但這個(gè)查詢(xún)卻是無(wú)效的。

參見(jiàn)

在 Django 的單元測(cè)試 OR查詢(xún)實(shí)例(OR lookups examples) 中展示了 Q 的用例。

對(duì)象比較

要比較兩個(gè)對(duì)象,就和 Python 一樣,使用雙等號(hào)運(yùn)算符:==。實(shí)際上比較的是兩個(gè) model 的主鍵值。

以上面的 Entry 為例,下面兩個(gè)語(yǔ)句是等價(jià)的:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id

如果 model 的主鍵名稱(chēng)不是 id,也沒(méi)關(guān)系。Django 會(huì)自動(dòng)比較主鍵的值,而不管他們的名稱(chēng)是什么。例如,如果一個(gè) model 的主鍵字段名稱(chēng)是 name,那么下面兩個(gè)語(yǔ)句是等價(jià)的:

>>> some_obj == other_obj
>>> some_obj.name == other_obj.name

對(duì)象刪除

刪除方法就是 delete()。它運(yùn)行時(shí)立即刪除對(duì)象而不返回任何值。例如:

e.delete()

你也可以一次性刪除多個(gè)對(duì)象。每個(gè) QuerySet 都有一個(gè) delete() 方法,它一次性刪除 QuerySet 中所有的對(duì)象。

例如,下面的代碼將刪除 pub_date 是2005年的 Entry 對(duì)象:

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

要牢記這一點(diǎn):無(wú)論在什么情況下,QuerySet 中的 delete() 方法都只使用一條 SQL 語(yǔ)句一次性刪除所有對(duì)象,而并不是分別刪除每個(gè)對(duì)象。如果你想使用在 model 中自定義的 delete() 方法,就要自行調(diào)用每個(gè)對(duì)象的delete 方法。(例如,遍歷 QuerySet,在每個(gè)對(duì)象上調(diào)用 delete()方法),而不是使用 QuerySet 中的 delete()方法。

在 Django 刪除對(duì)象時(shí),會(huì)模仿 SQL 約束 ON DELETE CASCADE 的行為,換句話(huà)說(shuō),刪除一個(gè)對(duì)象時(shí)也會(huì)刪除與它相關(guān)聯(lián)的外鍵對(duì)象。例如:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但并不適用于 Manager 本身。這是一種保護(hù)機(jī)制,是為了避免意外地調(diào)用 Entry.objects.delete() 方法導(dǎo)致 所有的 記錄被誤刪除。如果你確認(rèn)要?jiǎng)h除所有的對(duì)象,那么你必須顯式地調(diào)用:

Entry.objects.all().delete()

一次更新多個(gè)對(duì)象 (Updating multiple objects at once)

這部分是 Django 1.0 中新增的: 請(qǐng)查看版本文檔 有時(shí)你想對(duì) QuerySet 中的所有對(duì)象,一次更新某個(gè)字段的值。這個(gè)要求可以用 update() 方法完成。例如:

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

這種方法僅適用于非關(guān)系字段和 ForeignKey 外鍵字段。更新非關(guān)系字段時(shí),傳入的值應(yīng)該是一個(gè)常量。更新 ForeignKey 字段時(shí),傳入的值應(yīng)該是你想關(guān)聯(lián)的那個(gè)類(lèi)的某個(gè)實(shí)例。例如:

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

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

update() 方法也是即時(shí)生效,不返回任何值的(與 delete() 相似)。 在 QuerySet 進(jìn)行更新時(shí),唯一的限制就是一次只能更新一個(gè)數(shù)據(jù)表,就是當(dāng)前 model 的主表。所以不要嘗試更新關(guān)聯(lián)表和與此類(lèi)似的操作,因?yàn)檫@是不可能運(yùn)行的。

要小心的是: update() 方法是直接翻譯成一條 SQL 語(yǔ)句的。因此它是直接地一次完成所有更新。它不會(huì)調(diào)用你的 model 中的 save() 方法,也不會(huì)發(fā)出 pre_save 和 post_save 信號(hào)(這些信號(hào)在調(diào)用 save() 方法時(shí)產(chǎn)生)。如果你想保存 QuerySet 中的每個(gè)對(duì)象,并且調(diào)用每個(gè)對(duì)象各自的 save() 方法,那么你不必另外多寫(xiě)一個(gè)函式。只要遍歷這些對(duì)象,依次調(diào)用 save() 方法即可:

for item in my_queryset:
    item.save()

這部分是在 Django 1.1 中新增的: 請(qǐng)查看版本文檔 在調(diào)用 update 時(shí)可以使用 F() 對(duì)象 來(lái)把某個(gè)字段的值更新為另一個(gè)字段的值。這對(duì)于自增記數(shù)器是非常有用的。例如,給所有的博文 (entry) 的廣播數(shù) (pingback) 加一:

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

但是,與 F() 對(duì)象在查詢(xún)時(shí)所不同的是,在filter 和 exclude子句中,你不能在 F() 對(duì)象中引入關(guān)聯(lián)關(guān)系(NO-Join),你只能引用當(dāng)前 model 中要更新的字段。如果你在 F() 對(duì)象引入了Join 關(guān)系object,就會(huì)拋出 FieldError 異常:

# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))

對(duì)象關(guān)聯(lián)

當(dāng)你定義在 model 定義關(guān)系時(shí) (例如, ForeignKey, OneToOneField, 或 ManyToManyField),model 的實(shí)例自帶一套很方便的API以獲取關(guān)聯(lián)的對(duì)象。

以最上面的 models 為例,一個(gè) Entry 對(duì)象 e 能通過(guò) blog 屬性獲得相關(guān)聯(lián)的 Blog 對(duì)象: e.blog。

(在場(chǎng)景背后,這個(gè)功能是由 Python 的 descriptors 實(shí)現(xiàn)的。如果你對(duì)此感興趣,可以了解一下。)

Django 也提供反向獲取關(guān)聯(lián)對(duì)象的 API,就是由從被關(guān)聯(lián)的對(duì)象得到其定義關(guān)系的主對(duì)象。例如,一個(gè) Blog 類(lèi)的實(shí)例 b 對(duì)象通過(guò) entry_set 屬性得到所有相關(guān)聯(lián)的 Entry 對(duì)象列表: b.entry_set.all()。

這一節(jié)所有的例子都使用本頁(yè)頂部所列出的 Blog, Author 和 Entry model。

一對(duì)多關(guān)系

正向

如果一個(gè) model 有一個(gè) ForeignKey字段,我們只要通過(guò)使用關(guān)聯(lián) model 的名稱(chēng)就可以得到相關(guān)聯(lián)的外鍵對(duì)象。

例如:

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

你可以設(shè)置和獲得外鍵屬性。正如你所期望的,改變外鍵的行為并不引發(fā)數(shù)據(jù)庫(kù)操作,直到你調(diào)用 save()方法時(shí),才會(huì)保存到數(shù)據(jù)庫(kù)。例如:

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

如果外鍵字段 ForeignKey 有一個(gè) null=True 的設(shè)置(它允許外鍵接受空值 NULL),你可以賦給它空值 None 。例如:

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

在一對(duì)多關(guān)系中,第一次正向獲取關(guān)聯(lián)對(duì)象時(shí),關(guān)聯(lián)對(duì)象會(huì)被緩存。其后根據(jù)外鍵訪(fǎng)問(wèn)時(shí)這個(gè)實(shí)例,就會(huì)從緩存中獲得它。例如:

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

要注意的是,QuerySet 的 select_related() 方法提前將所有的一對(duì)多關(guān)系放入緩存中。例如:

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

逆向關(guān)聯(lián)

如果 model 有一個(gè) ForeignKey外鍵字段,那么外聯(lián) model 的實(shí)例可以通過(guò)訪(fǎng)問(wèn) Manager 來(lái)得到所有相關(guān)聯(lián)的源 model 的實(shí)例。默認(rèn)情況下,這個(gè) Manager 被命名為 FOO_set, 這里面的 FOO 就是源 model 的小寫(xiě)名稱(chēng)。這個(gè) Manager 返回 QuerySets,它是可過(guò)濾和可操作的,在上面 "對(duì)象獲取(Retrieving objects)" 有提及。

例如:

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

你可以通過(guò)在 ForeignKey() 的定義中設(shè)置 related_name 的值來(lái)覆寫(xiě) FOO_set 的名稱(chēng)。例如,如果 Entry model 中做一下更改: blog = ForeignKey(Blog, related_name='entries'),那么接下來(lái)就會(huì)如我們看到這般:

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

你不能在一個(gè)類(lèi)當(dāng)中訪(fǎng)問(wèn) ForeignKey Manager ;而必須通過(guò)類(lèi)的實(shí)例來(lái)訪(fǎng)問(wèn):

>>> Blog.entry_set
Traceback:
    ...
AttributeError: "Manager must be accessed via instance".

除了在上面 "對(duì)象獲取Retrieving objects" 一節(jié)中提到的 QuerySet 方法之外,F(xiàn)oreignKey Manager 還有如下一些方法。下面僅僅對(duì)它們做一個(gè)簡(jiǎn)短介紹,詳情請(qǐng)查看 related objects reference。

add(obj1, obj2, ...)

將某個(gè)特定的 model 對(duì)象添加到被關(guān)聯(lián)對(duì)象集合中。

create(**kwargs)

創(chuàng)建并保存一個(gè)新對(duì)象,然后將這個(gè)對(duì)象加被關(guān)聯(lián)對(duì)象的集合中,然后返回這個(gè)新對(duì)象。

remove(obj1, obj2, ...)

將某個(gè)特定的對(duì)象從被關(guān)聯(lián)對(duì)象集合中去除。

clear()

清空被關(guān)聯(lián)對(duì)象集合。 想一次指定關(guān)聯(lián)集合的成員,那么只要給關(guān)聯(lián)集合分配一個(gè)可迭代的對(duì)象即可。它可以包含對(duì)象的實(shí)例,也可以只包含主鍵的值。例如:

b = Blog.objects.get(id=1)
b.entry_set = [e1, e2]

在這個(gè)例子中,e1 和 e2 可以是完整的 Entry 實(shí)例,也可以是整型的主鍵值。

如果 clear() 方法是可用的,在迭代器(上例中就是一個(gè)列表)中的對(duì)象加入到 entry_set 之前,已存在于關(guān)聯(lián)集合中的所有對(duì)象將被清空。如果 clear() 方法 不可用,原有的關(guān)聯(lián)集合中的對(duì)象就不受影響,繼續(xù)存在。

這一節(jié)提到的每一個(gè) "reverse" 操作都是實(shí)時(shí)操作數(shù)據(jù)庫(kù)的,每一個(gè)添加,創(chuàng)建,刪除操作都會(huì)及時(shí)保存將結(jié)果保存到數(shù)據(jù)庫(kù)中。

多對(duì)多關(guān)系

在多對(duì)多關(guān)系的任何一方都可以使用 API 訪(fǎng)問(wèn)相關(guān)聯(lián)的另一方。多對(duì)多的 API 用起來(lái)和上面提到的 "逆向" 一對(duì)多關(guān)系關(guān)系非常相象。

唯一的差雖就在于屬性的命名: ManyToManyField 所在的 model (為了方便,我稱(chēng)之為源model A) 使用字段本身的名稱(chēng)來(lái)訪(fǎng)問(wèn)關(guān)聯(lián)對(duì)象;而被關(guān)聯(lián)的另一方則使用 A 的小寫(xiě)名稱(chēng)加上 '_set' 后綴(這與逆向的一對(duì)多關(guān)系非常相象)。

下面這個(gè)例子會(huì)讓人更容易理解:

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.

與 ForeignKey 一樣, ManyToManyField 也可以指定 related_name。在上面的例子中,如果 Entry 中的 ManyToManyField 指定 related_name='entries',那么接下來(lái)每個(gè) Author 實(shí)例的 entry_set 屬性都被 entries 所代替。

一對(duì)一關(guān)系

相對(duì)于多對(duì)一關(guān)系而言,一對(duì)一關(guān)系不是非常簡(jiǎn)單的。如果你在 model 中定義了一個(gè) OneToOneField 關(guān)系,那么你就可以用這個(gè)字段的名稱(chēng)做為屬性來(lái)訪(fǎng)問(wèn)其所關(guān)聯(lián)的對(duì)象。

例如:

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" 查詢(xún)不同的是,一對(duì)一關(guān)系的關(guān)聯(lián)對(duì)象也可以訪(fǎng)問(wèn) Manager 對(duì)象,但是這個(gè) Manager 表現(xiàn)一個(gè)單獨(dú)的對(duì)象,而不是一個(gè)列表:

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

如果一個(gè)空對(duì)象被賦予關(guān)聯(lián)關(guān)系,Django 就會(huì)拋出一個(gè) DoesNotExist 異常。

和你定義正向關(guān)聯(lián)所用的方式一樣,類(lèi)的實(shí)例也可以賦予逆向關(guān)聯(lián)方系:

e.entrydetail = ed

關(guān)系中的反向連接是如何做到的?

其他對(duì)象關(guān)系的映射(ORM)需要你在關(guān)聯(lián)雙方都定義關(guān)系。而 Django 的開(kāi)發(fā)者則認(rèn)為這違背了 DRY 原則 (Don't Repeat Yourself),所以 Django 只需要你在一方定義關(guān)系即可。

但僅由一個(gè) model 類(lèi)并不能知道其他 model 類(lèi)是如何與它關(guān)聯(lián)的,除非是其他 model 也被載入,那么這是如何辦到的?

答案就在于 INSTALLED_APPS 設(shè)置中。任何一個(gè) model 在第一次調(diào)用時(shí),Django 就會(huì)遍歷所有的 INSTALLED_APPS 的所有 models,并且在內(nèi)存中創(chuàng)建中必要的反向連接。本質(zhì)上來(lái)說(shuō),INSTALLED_APPS 的作用之一就是確認(rèn) Django 完整的 model 范圍。

在關(guān)聯(lián)對(duì)象上的查詢(xún)

包含關(guān)聯(lián)對(duì)象的查詢(xún)與包含普通字段值的查詢(xún)都遵循相同的規(guī)則。為某個(gè)查詢(xún)指定某個(gè)值的時(shí)候,你可以使用一個(gè)類(lèi)實(shí)例,也可以使用對(duì)象的主鍵值。

例如,如果你有一個(gè) Blog 對(duì)象 b ,它的 id=5, 下面三個(gè)查詢(xún)是一樣的:

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

直接使用SQL

如果你發(fā)現(xiàn)某個(gè) SQL 查詢(xún)用 Django 的數(shù)據(jù)庫(kù)映射來(lái)處理會(huì)非常復(fù)雜的話(huà),你可以使用直接寫(xiě) SQL 來(lái)完成。

建議的方式是在你的 model 自定義方法或是自定義 model 的 manager 方法來(lái)運(yùn)行查詢(xún)。雖然 Django 不要求數(shù)據(jù)操作必須在 model 層中執(zhí)行。但是把你的商業(yè)邏輯代碼放在一個(gè)地方,從代碼組織的角度來(lái)看,也是十分明智的。詳情請(qǐng)查看 執(zhí)行原生SQL查詢(xún)(Performing raw SQL queries).

最后,要注意的是,Django的數(shù)據(jù)操作層僅僅是訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的一個(gè)接口。你可以用其他的工具,編程語(yǔ)言,數(shù)據(jù)庫(kù)框架來(lái)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。對(duì)你的數(shù)據(jù)庫(kù)而言,沒(méi)什么是非用 Django 不可的。

譯者:Django 文檔協(xié)作翻譯小組,原文:Executing queries。

本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。

Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。

上一篇:系統(tǒng)檢查框架下一篇:中間件