Django中的查詢

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 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內(nèi)容