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