新聞中心
當(dāng)你在模型中定義了關(guān)聯(lián)關(guān)系(如 ?ForeignKey?, ?OneToOneField? 或 ?ManyToManyField?),該模型的實(shí)例將會(huì)自動(dòng)獲取一套 API,能快捷地訪問(wèn)關(guān)聯(lián)對(duì)象。

拿本文開(kāi)始的模型做例子,一個(gè) ?Entry ?對(duì)象 ?e ?通過(guò) ?blog ?屬性獲取其關(guān)聯(lián)的 ?Blog ?對(duì)象: ?e.blog?。
Django 也提供了從關(guān)聯(lián)關(guān)系 另一邊 訪問(wèn)的 API —— 從被關(guān)聯(lián)模型到定義關(guān)聯(lián)關(guān)系的模型的連接。例如,一個(gè) ?Blog ?對(duì)象 ?b ?能通過(guò) ?entry_set ?屬性 ?b.entry_set.all()? 訪問(wèn)包含所有關(guān)聯(lián) ?Entry ?對(duì)象的列表。
本章節(jié)中的所有例子都是用了本頁(yè)開(kāi)頭定義的 ?Blog?, ?Author ?和 ?Entry ?模型。
一對(duì)多關(guān)聯(lián)
正向訪問(wèn)
若模型有個(gè) ?ForeignKey?,該模型的實(shí)例能通過(guò)其屬性訪問(wèn)關(guān)聯(lián)(外部的)對(duì)象。例如:
>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.你可以通過(guò) ?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 ...;"首次通過(guò)正向一對(duì)多關(guān)聯(lián)訪問(wèn)關(guān)聯(lián)對(duì)象時(shí)會(huì)緩存關(guān)聯(lián)關(guān)系。后續(xù)在同一對(duì)象上通過(guò)外鍵的訪問(wèn)也會(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.“反向”關(guān)聯(lián)
若模型有 ?ForeignKey?,外鍵關(guān)聯(lián)的模型實(shí)例將能訪問(wèn) ?Manager?,后者會(huì)返回第一個(gè)模型的所有實(shí)例。默認(rèn)情況下,該 ?Manager ?名為 ?FOO_set?, ?FOO ?即源模型名的小寫(xiě)形式。 ?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ù)重寫(xiě)這個(gè) ?FOO_set? 名。例如,若修改 ?Entry ?模型為 ?blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries')?,前文示例代碼會(huì)看起來(lái)像這樣:
>>> 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)過(guò)濾行為,該行為會(huì)應(yīng)用到 對(duì)?all()? 的調(diào)用中。
指定一個(gè)自定義反向管理也允許你調(diào)用模型自定義方法:
b.entry_set(manager='entries').is_published()管理關(guān)聯(lián)對(duì)象的額外方法
?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)
多對(duì)多關(guān)聯(lián)的兩端均自動(dòng)獲取訪問(wèn)另一端的 API。該 API 的工作方式類似上面的 “反向” 一對(duì)多關(guān)聯(lián)。
不同點(diǎn)在為屬性命名上:定義了 ?ManyToManyField ?的模型使用字段名作為屬性名,而 “反向” 模型使用源模型名的小寫(xiě)形式,加上? '_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)與多對(duì)一關(guān)聯(lián)非常類似。若在模型中定義了 ?OneToOneField?,該模型的實(shí)例只需通過(guò)其屬性就能訪問(wèn)關(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ì)象也能訪問(wèn) ?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ò)為正向關(guān)聯(lián)指定關(guān)聯(lián)對(duì)象一樣的方式指定給反向關(guān)聯(lián):
e.entrydetail = ed反向關(guān)聯(lián)的實(shí)現(xiàn)
其它對(duì)象關(guān)聯(lián)映射實(shí)現(xiàn)要求你在兩邊都定義關(guān)聯(lián)關(guān)系。而 Django 開(kāi)發(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 ?模塊。無(wú)論何時(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ā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 分享名稱:創(chuàng)新互聯(lián)Django4.0教程:Django4.0執(zhí)行查詢-關(guān)聯(lián)對(duì)象
URL標(biāo)題:http://www.5511xx.com/article/dhgsegi.html


咨詢
建站咨詢
