W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
當(dāng)你在模型中定義了關(guān)聯(lián)關(guān)系(如 ?ForeignKey
?, ?OneToOneField
? 或 ?ManyToManyField
?),該模型的實(shí)例將會(huì)自動(dòng)獲取一套 API,能快捷地訪問關(guān)聯(lián)對(duì)象。
拿本文開始的模型做例子,一個(gè) ?Entry
?對(duì)象 ?e
?通過 ?blog
?屬性獲取其關(guān)聯(lián)的 ?Blog
?對(duì)象: ?e.blog
?。
Django 也提供了從關(guān)聯(lián)關(guān)系 另一邊 訪問的 API —— 從被關(guān)聯(lián)模型到定義關(guān)聯(lián)關(guān)系的模型的連接。例如,一個(gè) ?Blog
?對(duì)象 ?b
?能通過 ?entry_set
?屬性 ?b.entry_set.all()
? 訪問包含所有關(guān)聯(lián) ?Entry
?對(duì)象的列表。
本章節(jié)中的所有例子都是用了本頁(yè)開頭定義的 ?Blog
?, ?Author
?和 ?Entry
?模型。
若模型有個(gè) ?ForeignKey
?,該模型的實(shí)例能通過其屬性訪問關(guān)聯(lián)(外部的)對(duì)象。例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.
你可以通過 ?foreign-key
? 屬性獲取和設(shè)置值。對(duì)外鍵的修改直到你調(diào)用 ?save()
? 后才會(huì)被存入數(shù)據(jù)庫(kù)。例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()
若 ?ForeignKey
?字段配置了 ?null=True
? (即其允許 ?NULL
?值),你可以指定值為 ?None
?移除關(guān)聯(lián)。例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
首次通過正向一對(duì)多關(guān)聯(lián)訪問關(guān)聯(lián)對(duì)象時(shí)會(huì)緩存關(guān)聯(lián)關(guān)系。后續(xù)在同一對(duì)象上通過外鍵的訪問也會(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.
注意:?select_related() QuerySet
? 方法會(huì)預(yù)先用所有一對(duì)多關(guān)聯(lián)對(duì)象填充緩存。例如:
>>> 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.
若模型有 ?ForeignKey
?,外鍵關(guān)聯(lián)的模型實(shí)例將能訪問 ?Manager
?,后者會(huì)返回第一個(gè)模型的所有實(shí)例。默認(rèn)情況下,該 ?Manager
?名為 ?FOO_set
?, ?FOO
?即源模型名的小寫形式。 ?Manager
?返回 ?QuerySets
?,后者能以 “檢索對(duì)象” 章節(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ù)重寫這個(gè) ?FOO_set
? 名。例如,若修改 ?Entry
?模型為 ?blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')
?,前文示例代碼會(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()
?RelatedManager
?反向關(guān)聯(lián)的默認(rèn)實(shí)現(xiàn)是該模型默認(rèn)管理器 一個(gè)實(shí)例。若你想為某個(gè)查詢指定一個(gè)不同的管理器,可以使用如下語(yǔ)法:
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()
? 方法執(zhí)行了默認(rèn)過濾行為,該行為會(huì)應(yīng)用到 對(duì)?all()
? 的調(diào)用中。
指定一個(gè)自定義反向管理也允許你調(diào)用模型自定義方法:
b.entry_set(manager='entries').is_published()
?ForeignKey Manager
?還有方法能處理關(guān)聯(lián)對(duì)象集合。除了上面的 “檢索對(duì)象” 中定義的 ?QuerySet
?方法以外,以下是每項(xiàng)的簡(jiǎn)要介紹:
add(obj1, obj2, ...)
?:將特定的模型對(duì)象加入關(guān)聯(lián)對(duì)象集合。create(**kwargs)
?:創(chuàng)建一個(gè)新對(duì)象,保存,并將其放入關(guān)聯(lián)對(duì)象集合中。返回新創(chuàng)建的對(duì)象。remove(obj1, obj2, ...)
?:從關(guān)聯(lián)對(duì)象集合刪除指定模型對(duì)象。clear()
?:從關(guān)聯(lián)對(duì)象集合刪除所有對(duì)象。set(objs)
?:替換關(guān)聯(lián)對(duì)象集合要指定關(guān)聯(lián)集合的成員,調(diào)用 ?set()
? 方法,并傳入可迭代的對(duì)象實(shí)例集合。例如,若 ?e1
?和 ?e2
?都是 ?Entry
?實(shí)例:
b = Blog.objects.get(id=1)
b.entry_set.set([e1, e2])
若能使用 ?clear()
?方法, ?entry_set
?中所有舊對(duì)象會(huì)在將可迭代集合(本例中是個(gè)列表)中的對(duì)象加入其中之前被刪除。若 不能 使用 ?clear()
? 方法,添加新對(duì)象時(shí)不會(huì)刪除舊對(duì)象。
多對(duì)多關(guān)聯(lián)的兩端均自動(dòng)獲取訪問另一端的 API。該 API 的工作方式類似上面的 “反向” 一對(duì)多關(guān)聯(lián)。
不同點(diǎn)在為屬性命名上:定義了 ?ManyToManyField
?的模型使用字段名作為屬性名,而 “反向” 模型使用源模型名的小寫形式,加上? '_set'
? (就像反向一對(duì)多關(guān)聯(lián)一樣)。
例如:
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'
?,隨后每個(gè) ?Author
?實(shí)例會(huì)擁有一個(gè) ?entries
?屬性,而不是
?entry_set
?。
另一個(gè)與一對(duì)多關(guān)聯(lián)不同的地方是,除了模型實(shí)例以外,多對(duì)多關(guān)聯(lián)中的? add()
?, ?set()
? 和 ?remove()
? 方法能接收主鍵值。例如,若 ?e
?和 ?e2
?是 ?Entry
?的實(shí)例,以下兩種 ?set()
? 調(diào)用結(jié)果一致:
a = Author.objects.get(id=5)
a.entry_set.set([e1, e2])
a.entry_set.set([e1.pk, e2.pk])
一對(duì)一關(guān)聯(lián)與多對(duì)一關(guān)聯(lián)非常類似。若在模型中定義了 ?OneToOneField
?,該模型的實(shí)例只需通過其屬性就能訪問關(guān)聯(lián)對(duì)象。
例如:
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.
不同點(diǎn)在于 “反向” 查詢。一對(duì)一關(guān)聯(lián)所關(guān)聯(lián)的對(duì)象也能訪問 ?Manager
?對(duì)象,但這個(gè) ?Manager
?僅代表一個(gè)對(duì)象,而不是對(duì)象的集合:
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object
若未為關(guān)聯(lián)關(guān)系指定對(duì)象,Django 會(huì)拋出 ?DoesNotExist
? 異常。
實(shí)例能通過為正向關(guān)聯(lián)指定關(guān)聯(lián)對(duì)象一樣的方式指定給反向關(guān)聯(lián):
e.entrydetail = ed
其它對(duì)象關(guān)聯(lián)映射實(shí)現(xiàn)要求你在兩邊都定義關(guān)聯(lián)關(guān)系。而 Django 開發(fā)者堅(jiān)信這違反了 ?DRY
?原則(不要自我重復(fù)),故 Django 僅要求你在一端定義關(guān)聯(lián)關(guān)系。
但這是如何實(shí)現(xiàn)的呢,給你一個(gè)模型類,模型類并不知道是否有其它模型類關(guān)聯(lián)它,直到其它模型類被加載?
答案在于 應(yīng)用注冊(cè)。 Django 啟動(dòng)時(shí),它會(huì)導(dǎo)入 ?INSTALLED_APPS
?列出的每個(gè)應(yīng)用,和每個(gè)應(yīng)用中的 ?model
?模塊。無論何時(shí)創(chuàng)建了一個(gè)新模型類,Django 為每個(gè)關(guān)聯(lián)模型添加反向關(guān)聯(lián)。若被關(guān)聯(lián)的模型未被導(dǎo)入,Django 會(huì)持續(xù)追蹤這些關(guān)聯(lián),并在關(guān)聯(lián)模型被導(dǎo)入時(shí)添加關(guān)聯(lián)關(guān)系。
出于這個(gè)原因,包含你所使用的所有模型的應(yīng)用必須列在 ?INSTALLED_APPS
?中。否則,反向關(guān)聯(lián)可能不會(huì)正常工作。
涉及關(guān)聯(lián)對(duì)象的查詢與涉及普通字段的查詢遵守同樣的規(guī)則。未查詢條件指定值時(shí),你可以使用對(duì)象實(shí)例,或該實(shí)例的主鍵。
例如,若有個(gè)博客對(duì)象 ?b
?,其 ?id=5
?,以下三種查詢是一樣的:
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
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: