Django 1.8.2 文檔Home | Table of contents | Index | Modules? previous | up | next ?執(zhí)行查詢?一旦你建立好數(shù)據(jù)模型,Django 會(huì)自動(dòng)為你生成一套數(shù)據(jù)庫抽象的API,可以讓你創(chuàng)建、檢索、更新和刪除對象。這篇文檔闡述如何使用這些API。 關(guān)于模型查詢所有選項(xiàng)的完整細(xì)節(jié),請見數(shù)據(jù)模型參考。在整個(gè)文檔(以及參考)中,我們將引用下面的模型,它構(gòu)成一個(gè)博客應(yīng)用:
from django.db import modelsclass Blog(models.Model):? ? name = models.CharField(max_length=100)? ? tagline = models.TextField()? ? def __str__(self):? ? ? ? ? ? ? # __unicode__ on Python 2? ? ? ? return self.nameclass Author(models.Model):? ? name = models.CharField(max_length=50)? ? email = models.EmailField()? ? def __str__(self):? ? ? ? ? ? ? # __unicode__ on Python 2? ? ? ? return self.nameclass 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)建對象
?Django 使用一種直觀的方式把數(shù)據(jù)庫表中的數(shù)據(jù)表示成Python 對象:一個(gè)模型類代表數(shù)據(jù)庫中的一個(gè)表,一個(gè)模型類的實(shí)例代表這個(gè)數(shù)據(jù)庫表中的一條特定的記錄。使用關(guān)鍵字參數(shù)實(shí)例化模型實(shí)例來創(chuàng)建一個(gè)對象,然后調(diào)用save() 把它保存到數(shù)據(jù)庫中。假設(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ì)訪問數(shù)據(jù)庫。save() 方法沒有返回值。請參見save()方法帶有一些高級選項(xiàng),它們沒有在這里給出。完整的細(xì)節(jié)請見save() 文檔。如果你想只用一條語句創(chuàng)建并保存一個(gè)對象,使用create()方法。
保存對象的改動(dòng)
?要保存對數(shù)據(jù)庫中已存在的對象的改動(dòng),請使用save()。假設(shè)Blog 的一個(gè)實(shí)例b5 已經(jīng)被保存在數(shù)據(jù)庫中,下面這個(gè)例子將更改它的name 并且更新數(shù)據(jù)庫中的記錄:>>> b5.name = 'New name'>>> b5.save()上面的代碼在背后執(zhí)行SQL 的UPDATE語句。在你顯式調(diào)用save()之前,Django不會(huì)訪問數(shù)據(jù)庫。保存ForeignKey和ManyToManyField字段?更新ForeignKey 字段的方式和保存普通字段相同 —— 只要把一個(gè)正確類型的對象賦值給該字段。下面的例子更新了Entry 類的實(shí)例entry 的blog 屬性,假設(shè)Entry 和Blog 分別已經(jīng)有一個(gè)正確的實(shí)例保存在數(shù)據(jù)庫中(所以我們可以像下面這樣獲取它們):>>> 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()方法來增加關(guān)聯(lián)關(guān)系的一條記錄。下面這個(gè)例子向entry 對象添加Author 類的實(shí)例joe:>>> from blog.models import Author>>> joe = Author.objects.create(name="Joe")>>> entry.authors.add(joe)為了在一條語句中,向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ò)誤類型的對象時(shí)報(bào)錯(cuò)。獲取對象?通過模型中的管理器構(gòu)造一個(gè)查詢集,來從你的數(shù)據(jù)庫中獲取對象。查詢集表示從數(shù)據(jù)庫中取出來的對象的集合。它可以含有零個(gè)、一個(gè)或者多個(gè)過濾器。過濾器基于所給的參數(shù)限制查詢的結(jié)果。 從SQL 的角度,查詢集和SELECT 語句等價(jià),過濾器是像WHERE 和LIMIT 一樣的限制子句。你可以從模型的管理器那里取得查詢集。每個(gè)模型都至少有一個(gè)管理器,它默認(rèn)命名為objects。通過模型類來直接訪問它,像這樣:
>>> Blog.objects>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
注
管理器只可以通過模型的類訪問,而不可以通過模型的實(shí)例訪問,目的是為了強(qiáng)制區(qū)分“表級別”的操作和“記錄級別”的操作。
對于一個(gè)模型來說,管理器是查詢集的主要來源。例如,Blog.objects.all() 返回包含數(shù)據(jù)庫中所有Blog 對象的一個(gè)查詢集。
獲取所有對象?
獲取一個(gè)表中所有對象的最簡單的方式是全部獲取。可以使用管理器的all() 方法:
>>> all_entries = Entry.objects.all()
all()方法返回包含數(shù)據(jù)庫中所有對象的一個(gè)查詢集。
使用過濾器獲取特定對象?
all() 方法返回了一個(gè)包含數(shù)據(jù)庫表中所有記錄查詢集。但在通常情況下,你往往想要獲取的是完整數(shù)據(jù)集的一個(gè)子集。
要?jiǎng)?chuàng)建這樣一個(gè)子集,你需要在原始的的查詢集上增加一些過濾條件。兩個(gè)最普遍的途徑是:
filter(**kwargs)
返回一個(gè)新的查詢集,它包含滿足查詢參數(shù)的對象。
exclude(**kwargs)
返回一個(gè)新的查詢集,它包含不滿足查詢參數(shù)的對象。
查詢參數(shù)(上面函數(shù)定義中的**kwargs)需要滿足特定的格式,下面字段查詢一節(jié)中會(huì)提到。
舉個(gè)例子,要獲取年份為2006的所有文章的查詢集,可以使用filter()方法:
Entry.objects.filter(pub_date__year=2006)
利用默認(rèn)的管理器,它相當(dāng)于:
Entry.objects.all().filter(pub_date__year=2006)
鏈?zhǔn)竭^濾?
查詢集的篩選結(jié)果本身還是查詢集,所以可以將篩選語句鏈接在一起。像這樣:
>>> Entry.objects.filter(
...? ? headline__startswith='What'
... ).exclude(
...? ? pub_date__gte=datetime.date.today()
... ).filter(
...? ? pub_date__gte=datetime(2005, 1, 30)
... )
這個(gè)例子最開始獲取數(shù)據(jù)庫中所有對象的一個(gè)查詢集,之后增加一個(gè)過濾器,然后又增加一個(gè)排除,再之后又是另外一個(gè)過濾器。最后的結(jié)果仍然是一個(gè)查詢集,它包含標(biāo)題以”What“開頭、發(fā)布日期在2005年1月30日至當(dāng)天之間的所有記錄。
過濾后的查詢集是獨(dú)立的?
每次你篩選一個(gè)查詢集,得到的都是全新的另一個(gè)查詢集,它和之前的查詢集之間沒有任何綁定關(guān)系。每次篩選都會(huì)創(chuàng)建一個(gè)獨(dú)立的查詢集,它可以被存儲(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è)查詢集都是獨(dú)立的。第一個(gè)是一個(gè)基礎(chǔ)的查詢集,包含所有標(biāo)題以“What”開頭的記錄。第二個(gè)查詢集是第一個(gè)的子集,它增加另外一個(gè)限制條件,排除pub_date 為今天和將來的記錄。第三個(gè)查詢集同樣是第一個(gè)的子集,它增加另外一個(gè)限制條件,只選擇pub_date 為今天或?qū)淼挠涗洝T嫉牟樵兗?q1)不會(huì)受到篩選過程的影響。
查詢集是惰性執(zhí)行的?
查詢集 是惰性執(zhí)行的 —— 創(chuàng)建查詢集不會(huì)帶來任何數(shù)據(jù)庫的訪問。你可以將過濾器保持一整天,直到查詢集 需要求值時(shí),Django 才會(huì)真正運(yùn)行這個(gè)查詢。看下這個(gè)例子:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
雖然它看上去有三次數(shù)據(jù)庫訪問,但事實(shí)上只有在最后一行(print(q))時(shí)才訪問一次數(shù)據(jù)庫。一般來說,只有在“請求”查詢集 的結(jié)果時(shí)才會(huì)到數(shù)據(jù)庫中去獲取它們。當(dāng)你確實(shí)需要結(jié)果時(shí),查詢集 通過訪問數(shù)據(jù)庫來求值。 關(guān)于求值發(fā)生的準(zhǔn)確時(shí)間,參見何時(shí)計(jì)算查詢集。
通過get 獲取一個(gè)單一的對象?
filter() 始終給你一個(gè)查詢集,即使只有一個(gè)對象滿足查詢條件 —— 這種情況下,查詢集將只包含一個(gè)元素。
如果你知道只有一個(gè)對象滿足你的查詢,你可以使用管理器的get() 方法,它直接返回該對象:
>>> one_entry = Entry.objects.get(pk=1)
可以對get() 使用任何查詢表達(dá)式,和filter() 一樣 —— 同樣請查看下文的字段查詢。
注意,使用get() 和使用filter() 的切片[0] 有一點(diǎn)區(qū)別。如果沒有結(jié)果滿足查詢,get() 將引發(fā)一個(gè)DoesNotExist 異常。這個(gè)異常是正在查詢的模型類的一個(gè)屬性 —— 所以在上面的代碼中,如果沒有主鍵為1 的Entry 對象,Django 將引發(fā)一個(gè)Entry.DoesNotExist。
類似地,如果有多條記錄滿足get() 的查詢條件,Django 也將報(bào)錯(cuò)。這種情況將引發(fā)MultipleObjectsReturned,它同樣是模型類自身的一個(gè)屬性。
其它查詢集方法?
大多數(shù)情況下,需要從數(shù)據(jù)庫中查找對象時(shí),你會(huì)使用all()、 get()、filter() 和exclude()。 然而,這只是冰山一角;查詢集 方法的完整列表,請參見查詢集API 參考。
限制查詢集?
可以使用Python 的切片語法來限制查詢集記錄的數(shù)目 。它等同于SQL 的LIMIT 和OFFSET 子句。
例如,下面的語句返回前面5 個(gè)對象(LIMIT 5):
>>> Entry.objects.all()[:5]
下面這條語句返回第6 至第10 個(gè)對象(OFFSET 5 LIMIT 5):
>>> Entry.objects.all()[5:10]
不支持負(fù)的索引(例如Entry.objects.all()[-1])。
通常,查詢集 的切片返回一個(gè)新的查詢集 —— 它不會(huì)執(zhí)行查詢。有一個(gè)例外,是如果你使用Python 切片語法中"step"參數(shù)。例如,下面的語句將返回前10 個(gè)對象中每隔2個(gè)對象,它將真實(shí)執(zhí)行查詢:
>>> Entry.objects.all()[:10:2]
若要獲取一個(gè)單一的對象而不是一個(gè)列表(例如,SELECT foo FROM bar LIMIT 1),可以簡單地使用一個(gè)索引而不是切片。例如,下面的語句返回?cái)?shù)據(jù)庫中根據(jù)標(biāo)題排序后的第一條Entry:
>>> Entry.objects.order_by('headline')[0]
它大體等同于:
>>> Entry.objects.order_by('headline')[0:1].get()
然而請注意,如果沒有對象滿足給定的條件,第一條語句將引發(fā)IndexError而第二條語句將引發(fā)DoesNotExist。 更多細(xì)節(jié)參見get()。
字段查詢?
字段查詢是指如何指定SQL WHERE 子句的內(nèi)容。它們通過查詢集方法filter()、exclude() 和 get() 的關(guān)鍵字參數(shù)指定。
查詢的關(guān)鍵字參數(shù)的基本形式是field__lookuptype=value。(中間是兩個(gè)下劃線)。例如:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
翻譯成SQL(大體)是:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
這是如何實(shí)現(xiàn)的
Python 定義的函數(shù)可以接收任意的鍵/值對參數(shù),這些名稱和參數(shù)可以在運(yùn)行時(shí)求值。更多信息,參見Python 官方文檔中的關(guān)鍵字參數(shù)。
查詢條件中指定的字段必須是模型字段的名稱。但有一個(gè)例外,對于ForeignKey你可以使用字段名加上_id 后綴。在這種情況下,該參數(shù)的值應(yīng)該是外鍵的原始值。例如:
>>> Entry.objects.filter(blog_id=4)
如果你傳遞的是一個(gè)不合法的參數(shù),查詢函數(shù)將引發(fā) TypeError。
這些數(shù)據(jù)庫API 支持大約二十多種查詢的類型;在字段查詢參考 中可以找到完整的參考。為了讓你嘗嘗鮮,下面是一些你可能用到的常見查詢:
exact
“精確”匹配。例如:
>>> Entry.objects.get(headline__exact="Man bites dog")
將生成下面的SQL:
SELECT ... WHERE headline = 'Man bites dog';
如果你沒有提供查詢類型 —— 即如果你的關(guān)鍵字參數(shù)不包含雙下劃線 —— 默認(rèn)假定查詢類型是exact。
例如,下面的兩條語句相等:
>>> Blog.objects.get(id__exact=14)? # Explicit form
>>> Blog.objects.get(id=14)? ? ? ? # __exact is implied
這是為了方便,因?yàn)閑xact 查詢是最常見的情況。
iexact
大小寫不敏感的匹配。所以,查詢:
>>> Blog.objects.get(name__iexact="beatles blog")
將匹配標(biāo)題為"Beatles Blog"、"beatles blog" 甚至"BeAtlES blOG" 的Blog。
contains
大小寫敏感的包含關(guān)系測試。例如:
Entry.objects.get(headline__contains='Lennon')
大體可以翻譯成下面的SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
注意,這將匹配'Today Lennon honored' 但不能匹配'today lennon honored'。
還有一個(gè)大小寫不敏感的版本,icontains。
startswith, endswith
分別表示以XXX開頭和以XXX結(jié)尾。當(dāng)然還有大小寫不敏感的版本,叫做istartswith 和 iendswith。
同樣,這里只是表面。完整的參考可以在字段查詢參考中找到。
跨關(guān)聯(lián)關(guān)系的查詢?
Django 提供一種強(qiáng)大而又直觀的方式來“處理”查詢中的關(guān)聯(lián)關(guān)系,它在后臺自動(dòng)幫你處理JOIN。 若要跨越關(guān)聯(lián)關(guān)系,只需使用關(guān)聯(lián)的模型字段的名稱,并使用雙下劃線分隔,直至你想要的字段:
下面這個(gè)例子獲取所有Blog 的name 為'Beatles Blog' 的Entry 對象:
>>> Entry.objects.filter(blog__name='Beatles Blog')
這種跨越可以是任意的深度。
它還可以反向工作。若要引用一個(gè)“反向”的關(guān)系,只需要使用該模型的小寫的名稱。
下面的示例獲取所有的Blog 對象,它們至少有一個(gè)Entry 的headline 包含'Lennon':
>>> Blog.objects.filter(entry__headline__contains='Lennon')
如果你在多個(gè)關(guān)聯(lián)關(guān)系直接過濾而且其中某個(gè)中介模型沒有滿足過濾條件的值,Django 將把它當(dāng)做一個(gè)空的(所有的值都為NULL)但是合法的對象。這意味著不會(huì)有錯(cuò)誤引發(fā)。例如,在下面的過濾器中:
Blog.objects.filter(entry__authors__name='Lennon')
(如果有一個(gè)相關(guān)聯(lián)的Author 模型),如果Entry 中沒有找到對應(yīng)的author,那么它將當(dāng)作其沒有name,而不會(huì)因?yàn)闆]有author 引發(fā)一個(gè)錯(cuò)誤。通常,這就是你想要的。唯一可能讓你困惑的是當(dāng)你使用isnull 的時(shí)候。因此:
Blog.objects.filter(entry__authors__name__isnull=True)
返回的Blog 對象包括author __name 為空的Blog對象,以及author__name不為空但author__name關(guān)聯(lián)的entry __author 為空的對象。如果你不需要后者,你可以這樣寫:
Blog.objects.filter(entry__authors__isnull=False,
entry__authors__name__isnull=True)
跨越多值的關(guān)聯(lián)關(guān)系?
當(dāng)你基于ManyToManyField 或反向的ForeignKey 來過濾一個(gè)對象時(shí),有兩種不同種類的過濾器。考慮Blog/Entry 關(guān)聯(lián)關(guān)系(Blog 和 Entry 是一對多的關(guān)系)。我們可能想找出headline為“Lennon” 并且pub_date為'2008'年的Entry。或者我們可能想查詢headline為“Lennon” 的Entry或者pub_date為'2008'的Entry。因?yàn)閷?shí)際上有和單個(gè)Blog 相關(guān)聯(lián)的多個(gè)Entry,所以這兩個(gè)查詢在某些場景下都是有可能并有意義的。
ManyToManyField 有類似的情況。例如,如果Entry 有一個(gè)ManyToManyField 叫做 tags,我們可能想找到tag 叫做“music” 和“bands” 的Entry,或者我們想找一個(gè)tag 名為“music” 且狀態(tài)為“public”的Entry。
對于這兩種情況,Django 有種一致的方法來處理filter() 調(diào)用。一個(gè)filter() 調(diào)用中的所有參數(shù)會(huì)同時(shí)應(yīng)用以過濾出滿足所有要求的記錄。接下來的filter() 調(diào)用進(jìn)一步限制對象集,但是對于多值關(guān)系,它們應(yīng)用到與主模型關(guān)聯(lián)的對象,而不是應(yīng)用到前一個(gè)filter() 調(diào)用選擇出來的對象。
這些聽起來可能有點(diǎn)混亂,所以希望展示一個(gè)例子使它變得更清晰。選擇所有包含同時(shí)滿足兩個(gè)條件的entry的blog,這兩個(gè)條件是headline 包含Lennon 和發(fā)表時(shí)間是2008 (同一個(gè)entry 滿足兩個(gè)條件),我們的代碼是:
Blog.objects.filter(entry__headline__contains='Lennon',
entry__pub_date__year=2008)
從所有的blog模型實(shí)例中選擇滿足以下條件的blog實(shí)例:blog的enrty的headline屬性值是“Lennon”,或者entry的發(fā)表時(shí)間是2008(兩個(gè)條件至少滿足一個(gè),也可以同時(shí)滿足),我們的代碼是:
Blog.objects.filter(entry__headline__contains='Lennon').filter(
entry__pub_date__year=2008)
假設(shè)這里有一個(gè)blog擁有一條包含'Lennon'的entries條目和一條來自2008的entries條目,但是沒有一條來自2008并且包含"Lennon"的entries條目。第一個(gè)查詢不會(huì)返回任何blog,第二個(gè)查詢將會(huì)返回一個(gè)blog。
在第二個(gè)例子中, 第一個(gè)filter 限定查詢集中的blog 與headline 包含“Lennon” 的entry 關(guān)聯(lián)。第二個(gè)filter 又 限定查詢集中的blog ,這些blog關(guān)聯(lián)的entry 的發(fā)表時(shí)間是2008。(譯者注:難點(diǎn)在如何理解further這個(gè)詞!)第二個(gè)filter 過濾出來的entry 與第一個(gè)filter 過濾出來的entry 可能相同也可能不同。每個(gè)filter 語句過濾的是Blog,而不是Entry。
注
跨越多值關(guān)系的filter() 查詢的行為,與exclude() 實(shí)現(xiàn)的不同。單個(gè)exclude() 調(diào)用中的條件不必引用同一個(gè)記錄。
例如,下面的查詢排除headline 中包含“Lennon”的Entry和在2008 年發(fā)布的Entry:
Blog.objects.exclude(
entry__headline__contains='Lennon',
entry__pub_date__year=2008,
)
然而,這與使用filter() 的行為不同,它不是排除同時(shí)滿足兩個(gè)條件的Entry。為了實(shí)現(xiàn)這點(diǎn),即選擇的Blog中不包含在2008年發(fā)布且healine 中帶有“Lennon” 的Entry,你需要編寫兩個(gè)查詢:
Blog.objects.exclude(
entry=Entry.objects.filter(
headline__contains='Lennon',
pub_date__year=2008,
),
)
Filter 可以引用模型的字段?
到目前為止給出的示例中,我們構(gòu)造過將模型字段與常量進(jìn)行比較的filter。但是,如果你想將模型的一個(gè)字段與同一個(gè)模型的另外一個(gè)字段進(jìn)行比較該怎么辦?
Django 提供F 表達(dá)式 來允許這樣的比較。F() 返回的實(shí)例用作查詢內(nèi)部對模型字段的引用。這些引用可以用于查詢的filter 中來比較相同模型實(shí)例上不同字段之間值的比較。
例如,為了查找comments 數(shù)目多于pingbacks 的Entry,我們將構(gòu)造一個(gè)F() 對象來引用pingback 數(shù)目,并在查詢中使用該F() 對象:
>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
Django 支持對F() 對象使用加法、減法、乘法、除法、取模以及冪計(jì)算等算術(shù)操作,兩個(gè)操作數(shù)可以都是常數(shù)和其它F() 對象。為了查找comments 數(shù)目比pingbacks 兩倍還要多的Entry,我們將查詢修改為:
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
New in Django 1.7:
添加 ** 操作符。
為了查詢r(jià)ating 比pingback 和comment 數(shù)目總和要小的Entry,我們將這樣查詢:
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
你還可以在F() 對象中使用雙下劃線標(biāo)記來跨越關(guān)聯(lián)關(guān)系。帶有雙下劃線的F() 對象將引入任何需要的join 操作以訪問關(guān)聯(lián)的對象。例如,如要獲取author 的名字與blog 名字相同的Entry,我們可以這樣查詢:
>>> Entry.objects.filter(authors__name=F('blog__name'))
對于date 和date/time 字段,你可以給它們加上或減去一個(gè)timedelta 對象。下面的例子將返回發(fā)布超過3天后被修改的所有Entry:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
F() 對象支持.bitand() 和.bitor() 兩種位操作,例如:
>>> F('somefield').bitand(16)
查詢的快捷方式pk?
為了方便,Django 提供一個(gè)查詢快捷方式pk ,它表示“primary key” 的意思。
在Blog 模型示例中,主鍵是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 查詢 —— 任何查詢類型都可以與pk 結(jié)合來完成一個(gè)模型上對主鍵的查詢:
# 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查詢在join 中也可以工作。例如,下面三個(gè)語句是等同的:
>>> 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
轉(zhuǎn)義LIKE 語句中的百分號和下劃線?
與LIKE SQL 語句等同的字段查詢(iexact、 contains、icontains、startswith、 istartswith、endswith 和iendswith)將自動(dòng)轉(zhuǎn)義在LIKE 語句中使用的兩個(gè)特殊的字符 —— 百分號和下劃線。(在LIKE 語句中,百分號通配符表示多個(gè)字符,下劃線通配符表示單個(gè)字符)。
這意味著語句將很直觀,不會(huì)顯得太抽象。例如,要獲取包含一個(gè)百分號的所有的Entry,只需要像其它任何字符一樣使用百分號:
>>> Entry.objects.filter(headline__contains='%')
Django 會(huì)幫你轉(zhuǎn)義;生成的SQL 看上去會(huì)是這樣:
SELECT ... WHERE headline LIKE '%\%%';
對于下劃線是同樣的道理。百分號和下劃線都會(huì)透明地幫你處理。
緩存和查詢集?
每個(gè)查詢集都包含一個(gè)緩存來最小化對數(shù)據(jù)庫的訪問。理解它是如何工作的將讓你編寫最高效的代碼。
在一個(gè)新創(chuàng)建的查詢集中,緩存為空。首次對查詢集進(jìn)行求值 —— 同時(shí)發(fā)生數(shù)據(jù)庫查詢 ——Django 將保存查詢的結(jié)果到查詢集的緩存中并返回明確請求的結(jié)果(例如,如果正在迭代查詢集,則返回下一個(gè)結(jié)果)。接下來對該查詢集 的求值將重用緩存的結(jié)果。
請牢記這個(gè)緩存行為,因?yàn)閷Σ樵兗褂貌划?dāng)?shù)脑挘鼤?huì)坑你的。例如,下面的語句創(chuàng)建兩個(gè)查詢集,對它們求值,然后扔掉它們:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
這意味著相同的數(shù)據(jù)庫查詢將執(zhí)行兩次,顯然倍增了你的數(shù)據(jù)庫負(fù)載。同時(shí),還有可能兩個(gè)結(jié)果列表并不包含相同的數(shù)據(jù)庫記錄,因?yàn)樵趦纱握埱笃陂g有可能有Entry被添加進(jìn)來或刪除掉。
為了避免這個(gè)問題,只需保存查詢集并重新使用它:
>>> queryset = Entry.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.
何時(shí)查詢集不會(huì)被緩存?
查詢集不會(huì)永遠(yuǎn)緩存它們的結(jié)果。當(dāng)只對查詢集的部分進(jìn)行求值時(shí)會(huì)檢查緩存, 但是如果這個(gè)部分不在緩存中,那么接下來查詢返回的記錄都將不會(huì)被緩存。特別地,這意味著使用切片或索引來限制查詢集將不會(huì)填充緩存。
例如,重復(fù)獲取查詢集對象中一個(gè)特定的索引將每次都查詢數(shù)據(jù)庫:
>>> queryset = Entry.objects.all()
>>> print queryset[5] # Queries the database
>>> print queryset[5] # Queries the database again
然而,如果已經(jīng)對全部查詢集求值過,則將檢查緩存:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print queryset[5] # Uses cache
>>> print queryset[5] # Uses cache
下面是一些其它例子,它們會(huì)使得全部的查詢集被求值并填充到緩存中:
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
注
簡單地打印查詢集不會(huì)填充緩存。因?yàn)開_repr__() 調(diào)用只返回全部查詢集的一個(gè)切片。
使用Q 對象進(jìn)行復(fù)雜的查詢?
filter() 等方法中的關(guān)鍵字參數(shù)查詢都是一起進(jìn)行“AND” 的。 如果你需要執(zhí)行更復(fù)雜的查詢(例如OR 語句),你可以使用Q 對象。
Q 對象 (django.db.models.Q) 對象用于封裝一組關(guān)鍵字參數(shù)。這些關(guān)鍵字參數(shù)就是上文“字段查詢” 中所提及的那些。
例如,下面的Q 對象封裝一個(gè)LIKE 查詢:
from django.db.models import Q
Q(question__startswith='What')
Q 對象可以使用& 和| 操作符組合起來。當(dāng)一個(gè)操作符在兩個(gè)Q 對象上使用時(shí),它產(chǎn)生一個(gè)新的Q 對象。
例如,下面的語句產(chǎn)生一個(gè)Q 對象,表示兩個(gè)"question__startswith" 查詢的“OR” :
Q(question__startswith='Who') | Q(question__startswith='What')
它等同于下面的SQL WHERE 子句:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
你可以組合& 和|? 操作符以及使用括號進(jìn)行分組來編寫任意復(fù)雜的Q 對象。同時(shí),Q 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
每個(gè)接受關(guān)鍵字參數(shù)的查詢函數(shù)(例如filter()、exclude()、get())都可以傳遞一個(gè)或多個(gè)Q 對象作為位置(不帶名的)參數(shù)。如果一個(gè)查詢函數(shù)有多個(gè)Q 對象參數(shù),這些參數(shù)的邏輯關(guān)系為“AND"。例如:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
... 大體上可以翻譯成這個(gè)SQL:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
查詢函數(shù)可以混合使用Q 對象和關(guān)鍵字參數(shù)。所有提供給查詢函數(shù)的參數(shù)(關(guān)鍵字參數(shù)或Q 對象)都將"AND”在一起。但是,如果出現(xiàn)Q 對象,它必須位于所有關(guān)鍵字參數(shù)的前面。例如:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who')
... 是一個(gè)合法的查詢,等同于前面的例子;但是:
# INVALID QUERY
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
... 是不合法的。
另見
Django 單元測試中的OR 查詢示例演示了幾種Q 的用法。
比較對象?
為了比較兩個(gè)模型實(shí)例,只需要使用標(biāo)準(zhǔn)的Python 比較操作符,即雙等于符號:==。在后臺,它會(huì)比較兩個(gè)模型主鍵的值。
利用上面的Entry 示例,下面兩個(gè)語句是等同的:
>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
如果模型的主鍵不叫id,也沒有問題。比較將始終使用主鍵,無論它叫什么。例如,如果模型的主鍵字段叫做name,下面的兩條語句是等同的:
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
刪除對象?
刪除方法,為了方便,就取名為delete()。這個(gè)方法將立即刪除對象且沒有返回值。例如:
e.delete()
你還可以批量刪除對象。每個(gè)查詢集 都有一個(gè)delete() 方法,它將刪除該查詢集中的所有成員。
例如,下面的語句刪除pub_date 為2005 的所有Entry 對象:
Entry.objects.filter(pub_date__year=2005).delete()
記住,這將盡可能地使用純SQL 執(zhí)行,所以這個(gè)過程中不需要調(diào)用每個(gè)對象實(shí)例的delete()方法。如果你給模型類提供了一個(gè)自定義的delete() 方法并希望確保它被調(diào)用,你需要手工刪除該模型的實(shí)例(例如,迭代查詢集并調(diào)用每個(gè)對象的delete())而不能使用查詢集的批量delete() 方法。
當(dāng)Django 刪除一個(gè)對象時(shí),它默認(rèn)使用SQL ON DELETE CASCADE 約束 —— 換句話講,任何有外鍵指向要?jiǎng)h除對象的對象將一起刪除。例如:
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
這種級聯(lián)的行為可以通過的ForeignKey 的on_delete 參數(shù)自定義。
注意,delete() 是唯一沒有在管理器 上暴露出來的查詢集方法。這是一個(gè)安全機(jī)制來防止你意外地請求Entry.objects.delete(),而刪除所有 的條目。如果你確實(shí)想刪除所有的對象,你必須明確地請求一個(gè)完全的查詢集:
Entry.objects.all().delete()
拷貝模型實(shí)例?
雖然沒有內(nèi)建的方法用于拷貝模型實(shí)例,但還是很容易創(chuàng)建一個(gè)新的實(shí)例并讓它的所有字段都拷貝過來。最簡單的方法是,只需要將pk 設(shè)置為None。利用我們的Blog 示例:
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1
blog.pk = None
blog.save() # blog.pk == 2
如果你用繼承,那么會(huì)復(fù)雜一些。考慮下面Blog 的子類:
class ThemeBlog(Blog):
theme = models.CharField(max_length=200)
django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3
由于繼承的工作方式,你必須設(shè)置pk 和 id 都為None:
django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4
這個(gè)過程不會(huì)拷貝關(guān)聯(lián)的對象。如果你想拷貝關(guān)聯(lián)關(guān)系,你必須編寫一些更多的代碼。在我們的例子中,Entry 有一個(gè)到Author 的多對多字段:
entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors = old_authors # saves new many2many relations
一次更新多個(gè)對象?
有時(shí)你想為一個(gè)查詢集中所有對象的某個(gè)字段都設(shè)置一個(gè)特定的值。這時(shí)你可以使用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)聯(lián)字段和ForeignKey 字段使用這個(gè)方法。若要更新一個(gè)非關(guān)聯(lián)字段,只需提供一個(gè)新的常數(shù)值。若要更新ForeignKey 字段,需設(shè)置新的值為你想指向的新的模型實(shí)例。例如:
>>> b = Blog.objects.get(pk=1)
# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)
update() 方法會(huì)立即執(zhí)行并返回查詢匹配的行數(shù)(如果有些行已經(jīng)具有新的值,返回的行數(shù)可能和被更新的行數(shù)不相等)。更新查詢集 唯一的限制是它只能訪問一個(gè)數(shù)據(jù)庫表,也就是模型的主表。你可以根據(jù)關(guān)聯(lián)的字段過濾,但是你只能更新模型主表中的列。例如:
>>> 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')
要注意update() 方法會(huì)直接轉(zhuǎn)換成一個(gè)SQL 語句。它是一個(gè)批量的直接更新操作。它不會(huì)運(yùn)行模型的save() 方法,或者發(fā)出pre_save 或 post_save信號(調(diào)用save()方法產(chǎn)生)或者查看auto_now 字段選項(xiàng)。如果你想保存查詢集中的每個(gè)條目并確保每個(gè)實(shí)例的save() 方法都被調(diào)用,你不需要使用任何特殊的函數(shù)來處理。只需要迭代它們并調(diào)用save():
for item in my_queryset:
item.save()
對update 的調(diào)用也可以使用F 表達(dá)式 來根據(jù)模型中的一個(gè)字段更新另外一個(gè)字段。這對于在當(dāng)前值的基礎(chǔ)上加上一個(gè)值特別有用。例如,增加Blog 中每個(gè)Entry 的pingback 個(gè)數(shù):
>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
然而,與filter 和exclude 子句中的F() 對象不同,在update 中你不可以使用F() 對象引入join —— 你只可以引用正在更新的模型的字段。如果你嘗試使用F() 對象引入一個(gè)join,將引發(fā)一個(gè)FieldError:
# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))
關(guān)聯(lián)的對象?
當(dāng)你在一個(gè)模型中定義一個(gè)關(guān)聯(lián)關(guān)系時(shí)(例如,F(xiàn)oreignKey、 OneToOneField 或ManyToManyField),該模型的實(shí)例將帶有一個(gè)方便的API 來訪問關(guān)聯(lián)的對象。
利用本頁頂部的模型,一個(gè)Entry 對象e 可以通過blog 屬性e.blog 獲取關(guān)聯(lián)的Blog 對象。
(在幕后,這個(gè)功能是通過Python 的描述器實(shí)現(xiàn)的。這應(yīng)該不會(huì)對你有什么真正的影響,但是這里我們指出它以滿足你的好奇)。
Django 還會(huì)創(chuàng)建API 用于訪問關(guān)聯(lián)關(guān)系的另一頭 —— 從關(guān)聯(lián)的模型訪問定義關(guān)聯(lián)關(guān)系的模型。例如,Blog 對象b 可以通過entry_set 屬性 b.entry_set.all()訪問與它關(guān)聯(lián)的所有Entry 對象。
這一節(jié)中的所有示例都將使用本頁頂部定義的Blog、 Author 和Entry 模型。
一對多關(guān)系?
前向查詢?
如果一個(gè)模型具有ForeignKey,那么該模型的實(shí)例將可以通過屬性訪問關(guān)聯(lián)的(外部)對象。
例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.
你可以通過外鍵屬性獲取和設(shè)置。和你預(yù)期的一樣,對外鍵的修改不會(huì)保存到數(shù)據(jù)庫中直至你調(diào)用save()。例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()
如果ForeignKey 字段有null=True 設(shè)置(即它允許NULL 值),你可以分配None 來刪除對應(yīng)的關(guān)聯(lián)性。例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
一對多關(guān)聯(lián)關(guān)系的前向訪問在第一次訪問關(guān)聯(lián)的對象時(shí)被緩存。以后對同一個(gè)對象的外鍵的訪問都使用緩存。例如:
>>> 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.
注意select_related() 查詢集方法遞歸地預(yù)填充所有的一對多關(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.
反向查詢?
如果模型有一個(gè)ForeignKey,那么該ForeignKey 所指的模型實(shí)例可以通過一個(gè)管理器返回前一個(gè)有ForeignKey的模型的所有實(shí)例。默認(rèn)情況下,這個(gè)管理器的名字為foo_set,其中foo 是源模型的小寫名稱。該管理器返回的查詢集可以用上一節(jié)提到的方式進(jìn)行過濾和操作。
例如:
>>> 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()
你可以在ForeignKey 定義時(shí)設(shè)置related_name 參數(shù)來覆蓋foo_set 的名稱。例如,如果Entry 模型改成blog = ForeignKey(Blog, related_name='entries'),那么上面的示例代碼應(yīng)該改成這樣:
>>> 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()
使用自定義的反向管理器?
New in Django 1.7.
默認(rèn)情況下,用于反向關(guān)聯(lián)關(guān)系的RelatedManager 是該模型默認(rèn)管理器 的子類。如果你想為一個(gè)查詢指定一個(gè)不同的管理器,你可以使用下面的語法:
from django.db import models
class Entry(models.Model):
#...
objects = models.Manager()? # Default Manager
entries = EntryManager()? ? # Custom Manager
b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()
如果EntryManager 在它的get_queryset() 方法中使用默認(rèn)的過濾,那么該過濾將適用于all() 調(diào)用。
當(dāng)然,指定一個(gè)自定義的管理器還可以讓你調(diào)用自定義的方法:
b.entry_set(manager='entries').is_published()
處理關(guān)聯(lián)對象的其它方法?
除了在上面”獲取對象“一節(jié)中定義的查詢集 方法之外,F(xiàn)oreignKey 管理器 還有其它方法用于處理關(guān)聯(lián)的對象集合。下面是每個(gè)方法的大概,完整的細(xì)節(jié)可以在關(guān)聯(lián)對象參考 中找到。
add(obj1, obj2, ...)
添加一指定的模型對象到關(guān)聯(lián)的對象集中。
create(**kwargs)
創(chuàng)建一個(gè)新的對象,將它保存并放在關(guān)聯(lián)的對象集中。返回新創(chuàng)建的對象。
remove(obj1, obj2, ...)
從關(guān)聯(lián)的對象集中刪除指定的模型對象。
clear()
從關(guān)聯(lián)的對象集中刪除所有的對象。
若要一次性給關(guān)聯(lián)的對象集賦值,只需要給它賦值一個(gè)可迭代的對象。這個(gè)可迭代的對象可以包含對象的實(shí)例,或者一個(gè)主鍵值的列表。例如:
b = Blog.objects.get(id=1)
b.entry_set = [e1, e2]
在這個(gè)例子中,e1 和e2 可以是Entry 實(shí)例,也可以是主鍵的整數(shù)值。
如果有clear() 方法,那么在將可迭代對象中的成員添加到集合中之前,將從entry_set 中刪除所有已經(jīng)存在的對象。如果沒有clear() 方法,那么將直接添加可迭代對象中的成員而不會(huì)刪除所有已存在的對象。
這一節(jié)中提到的每個(gè)”反向“操作都會(huì)立即對數(shù)據(jù)庫產(chǎn)生作用。每個(gè)添加、創(chuàng)建和刪除操作都會(huì)立即并自動(dòng)保存到數(shù)據(jù)庫中。
多對多關(guān)系?
多對多關(guān)系的兩端都會(huì)自動(dòng)獲得訪問另一端的API。這些API 的工作方式與上面提到的“方向”一對多關(guān)系一樣。
唯一的區(qū)別在于屬性的名稱:定義 ManyToManyField 的模型使用該字段的屬性名稱,而“反向”模型使用源模型的小寫名稱加上'_set' (和一對多關(guān)系一樣)。
一個(gè)例子可以讓它更好理解:
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',那么Author 實(shí)例將使用 entries 屬性而不是entry_set。
一對一關(guān)系?
一對一關(guān)系與多對一關(guān)系非常相似。如果你在模型中定義一個(gè)OneToOneField,該模型的實(shí)例將可以通過該模型的一個(gè)簡單屬性訪問關(guān)聯(lián)的模型。
例如:
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.
在“反向”查詢中有所不同。一對一關(guān)系中的關(guān)聯(lián)模型同樣具有一個(gè)管理器對象,但是該管理器表示一個(gè)單一的對象而不是對象的集合:
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object
如果沒有對象賦值給這個(gè)關(guān)聯(lián)關(guān)系,Django 將引發(fā)一個(gè)DoesNotExist 異常。
實(shí)例可以賦值給反向的關(guān)聯(lián)關(guān)系,方法和正向的關(guān)聯(lián)關(guān)系一樣:
e.entrydetail = ed
反向的關(guān)聯(lián)關(guān)系是如何實(shí)現(xiàn)的??
其它對象關(guān)系映射要求你在關(guān)聯(lián)關(guān)系的兩端都要定義。Django 的開發(fā)人員相信這是對DRY(不要重復(fù)你自己的代碼)原則的違背,所以Django 只要求你在一端定義關(guān)聯(lián)關(guān)系。
但是這怎么可能?因?yàn)橐粋€(gè)模型類直到其它模型類被加載之后才知道哪些模型類是關(guān)聯(lián)的。
答案在app registry 中。當(dāng)Django 啟動(dòng)時(shí),它導(dǎo)入INSTALLED_APPS 中列出的每個(gè)應(yīng)用,然后導(dǎo)入每個(gè)應(yīng)用中的models 模塊。每創(chuàng)建一個(gè)新的模型時(shí),Django 添加反向的關(guān)系到所有關(guān)聯(lián)的模型。如果關(guān)聯(lián)的模型還沒有導(dǎo)入,Django 將保存關(guān)聯(lián)關(guān)系的記錄并在最終關(guān)聯(lián)的模型導(dǎo)入時(shí)添加這些關(guān)聯(lián)關(guān)系。
由于這個(gè)原因,你使用的所有模型都定義在INSTALLED_APPS 列出的應(yīng)用中就顯得特別重要。否則,反向的關(guān)聯(lián)關(guān)系將不能正確工作。
通過關(guān)聯(lián)的對象進(jìn)行查詢?
在關(guān)聯(lián)對象字段上的查詢與正常字段的查詢遵循同樣的規(guī)則。當(dāng)你指定查詢需要匹配的一個(gè)值時(shí),你可以使用一個(gè)對象實(shí)例或者對象的主鍵的值。
例如,如果你有一個(gè)id=5 的Blog 對象b,下面的三個(gè)查詢將是完全一樣的:
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)需要編寫的SQL 查詢對于Django 的數(shù)據(jù)庫映射機(jī)制太復(fù)雜,你可以回歸到手工編寫SQL。Django 對于編寫原始的SQL 查詢有多個(gè)選項(xiàng);參見執(zhí)行原始的SQL 查詢。
最后,值得注意的是Django 的數(shù)據(jù)庫層只是數(shù)據(jù)庫的一個(gè)接口。你可以利用其它工具、編程語言或數(shù)據(jù)庫框架來訪問數(shù)據(jù)庫;對于數(shù)據(jù)庫,Django 沒有什么特別的地方。
2015年5月13日
? previous | up | next ?