管理器¶ class Manager ¶
管理器 是 Django 模型的資料庫操作介面。每個 Django 應用中的模型至少有一個 管理器 。
管理器 類的工作方式在 構造查詢 有詳細的描述。本文主要談談自訂 管理器 。 管理器名稱¶
預設情況下, Django 為每個模型類添加一個名為 objects 的 管理器 。可是如果你想把 objects 作為一個欄位的名稱或者你不想把 objects 作為 管理器 的名稱,那麼你可以在模型的基礎定義中重新命名 管理器 的名稱。重新命名的方法是在模型中定義一個 models.Manager() 類型的類屬性。例如:
from django.db import modelsclass Person(models.Model): #... people = models.Manager()
上例中,如果使用 Person.objects 會引發一個 AttributeError 異常,但是使用 Person.people.all() 則會返回一個所有 Person 對象的列表。 自訂管理器¶
通過擴充基礎 管理器 類可以在模型中使用自訂 管理器 ,並初始化 管理器 。
我們要使用自訂 管理器 有兩個目的 :增加額外的 管理器 方法和(或)修改 管理器 返回的原始 查詢集 。 增加額外的管理器方法¶
增加額外的 管理器 方法是為模型增加“表層級”功能的首選方式。(要增加如作用於模型對象中單個執行個體的“行層級”的功能請使用 模型方法 ,而不要使用 管理器 方法。)
自訂 管理器 方法可以返回任何東西,不一定要返回一個 查詢集 。
例如下面的自訂 管理器 提供一個 with_counts() 方法,這個方法返回一個包含所有 OpinionPoll 的列表,每個列表中的對象都有一個 num_responses 屬性,這個屬性代表一個合計數:
class PollManager(models.Manager): def with_counts(self): from django.db import connection cursor = connection.cursor() cursor.execute(""" SELECT p.id, p.question, p.poll_date, COUNT(*) FROM polls_opinionpoll p, polls_response r WHERE p.id = r.poll_id GROUP BY 1, 2, 3 ORDER BY 3 DESC""") result_list = [] for row in cursor.fetchall(): p = self.model(id=row[0], question=row[1], poll_date=row[2]) p.num_responses = row[3] result_list.append(p) return result_listclass OpinionPoll(models.Model): question = models.CharField(max_length=200) poll_date = models.DateField() objects = PollManager()class Response(models.Model): poll = models.ForeignKey(Poll) person_name = models.CharField(max_length=50) response = models.TextField()
使用上例中的模型,你可以使用 OpinionPoll.objects.with_counts() 來返回帶有 num_responses 屬性的 OpininPoll 對象列表。
另一個要注意的是 管理器 方法可以通過 self.model 來獲得其所依附的模型類。 修改原始管理器查詢集¶
一個 管理器 的基礎 查詢集 返回系統中的所有對象。例如,使用這個模型:
class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50)
...語句 Book.objects.all() 會返回資料庫中所有的書。
通過重載 Manager.get_query_set() 可以重載 管理器 的基礎 查詢集 。 get_query_set() 應當返回一個符合你需求的 查詢集 。
例如,以下模型有 兩個 管理器 ,一個返回所有對象,另一個只返回作者為 Roald Dah1 的對象:
# 首先定義管理器的子類。class DahlBookManager(models.Manager): def get_query_set(self): return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')# 顯式的把上面的子類安裝到 Book 模型中。class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager.
上例中, Book.objects.all() 會返回資料庫中所有書,但是 Book.dahl_objects.all() 只返回作者為Roald Dah1 的書。
當然,因為 get_query_set() 返回的是一個 查詢集 對象,所以可以對這個對象使用 filter() 、 exclude() 和所有其它 查詢集 方法。以下語句都是合法的:
Book.dahl_objects.all()Book.dahl_objects.filter(title='Matilda')Book.dahl_objects.count()
這個例子也展示了一個有趣的技巧:在同一個模型中使用多個管理器。你可以為一個模型附加任意多 Manager() 執行個體。 這是一個為模型定義常用“過濾器”的便捷方法。
例如:
class MaleManager(models.Manager): def get_query_set(self): return super(MaleManager, self).get_query_set().filter(sex='M')class FemaleManager(models.Manager): def get_query_set(self): return super(FemaleManager, self).get_query_set().filter(sex='F')class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female'))) people = models.Manager() men = MaleManager() women = FemaleManager()
這個例子讓你方便地使用 Person.men.all() 、 Person.women.all() 和 Person.people.all() ,獲得明確的結果。
如果你使用自訂 管理器 對象,那麼要注意第一個 管理器 (根據在模型定義的順序)有一個特殊的狀態。 Django 把第一個 管理器 作為預設的 管理器 ,並且 Django 的不同組成部分(包括 dumpdata )只使用模型的預設``管理器`` 。所以要仔細斟酌如何定義第一個 管理器 ,以免在查詢進無法獲得正確的結果。 使用管理器操作關聯對象¶
預設情況下,當操作關聯對象時 Django 使用一個“簡明”管理器類的執行個體(如 choice.poll ),而不是關聯對象本身的預設管理器。只有這樣 Django 才可以在任何情況下都能擷取關聯對象(預設的管理器可能會被過濾器過濾掉,變得不可操作)。
如果一般的簡明管理器類( django.db.models.Manager )不合你的胃口,那麼你可通過設定模型的 use_for_related_fields 屬性來使用你自己的。詳見 下文 。 自訂管理器和模型繼承¶
模型類的繼承時,模型的管理器也同時繼承有時候是不適合的。因為管理器通常是針對特定的類的。同時因為第一個管理器被視作 預設管理器 ,所以管理器是否同時繼承應當可以控制是非常重要的。所以 Django 是這樣處理自訂管理器和 模型繼承 的: 非抽象基類中的管理器是 不 繼承的。因為這種管理器通常是針對特定的非抽象基類的。如果你要重用非抽象基類中的管理器,那麼必須在其子類中顯式的重新定義這個管理器。 抽象基類的管理器總是被其子類繼承的。管理器的名稱使用 Python 的通常名稱處理方式(子類的名稱重載所有其他名稱;名稱由第一個父類而來等)。抽象基類就是用來其子類的通用資訊和行為的,所有其管理器天生就是用來處理通用的資訊的,理當被繼承。 如果模型類中聲明了管理器,那麼第一個聲明的管理器就是預設管理器。如果沒有聲明,且這個類是繼承自一個抽象基類,那麼繼承關係中第一個父抽象基類的預設管理器就是這個類的預設管理器。如果沒有顯式聲明任何管理器,那麼就會使用 Django 自身的一般預設管理器。
以上規則在多個模型中使用多個自訂管理器時提供了足夠的彈性。例如,假設有如下基類:
class AbstractBase(models.Model): ... objects = CustomManager() class Meta: abstract = True
如果子類中不聲明管理器,那麼父類中 objects 就會成為預設管理器:
class ChildA(AbstractBase): ... # 這個類的預設管理器是 CustomManager 。
如果要繼承 AbstractBase ,但想使用不同的預設管理器,那麼可以在子類中聲明自己的預設管理器:
class ChildB(AbstractBase): ... # 一個顯式定義的預設管理器。 default_manager = OtherManager()
上例中 default_manager 是預設管理器。 objects 管理器仍是可用的,但不是預設管理器。
最後,對於上例,假設想要添加額外的管理器,但是仍然要使用父類 AbstractBase 的管理器作為預設管理器。不能直接在子類中添加新的管理器,因為新添加的管理器會重載父類的管理器而成為預設的管理器。那麼必須先要顯式的聲明所有父類的管理器嗎。不必,可以把要添加的管理器放在另外一個基類中,然後在要繼承的基類 之後 引入另外這個基類:
class ExtraManager(models.Model): extra_manager = OtherManager() class Meta: abstract = Trueclass ChildC(AbstractBase, ExtraManager): ... # 預設管理器是 CustomManager ,但 OtherManager 作為 # “ extra_manager ”的屬性也可以使用。
實施要點¶
不論你在自訂 管理器 中添加了任何功能, 管理器 執行個體都必須可以被淺複製。例如以下代碼應當可以執行通過:
>>> import copy>>> manager = MyManager()>>> my_copy = copy.copy(manager)
在執行某些查詢時, Django 會執行淺複製,如果你的管理器無法被淺複製,那些查詢會失敗。
如果你的管理器不能被淺複製,大多數情況下不會有問題。但是,如果你重載了 __getattr__ 或管理器的一些私人方法,那麼你應當確認你的管理器可以被淺複製。 控制自動管理器類型¶
本文已經談了許多 Django 為你建立的管理器類:`預設管理器`_ 和用於 操作關聯對象 的“簡明”管理器。但是 Django 的運行還需要一些其它的簡明管理器。這些自動建立的管理器是 django.db.models.Manager 的執行個體。
本節我們會使用“自動管理器”來指代 Django 為你建立的管理器。“自動管理器”包括未定義管理器的模型的預設管理器和當操作關聯對象時臨時使用的管理器。
有時候使用預設的管理器不是一個正確的選擇。例如 Django 內建的 django.contrib.gis 應用中,所有 gis 模型必須使用一個特殊的管理器類( GeoManager ),因為模型需要一個特殊的查詢集( GeoQuerySet )來與資料庫互動。即有時候模型不需要自動建立的管理器,而只使用特定的管理器。
Django 通過設定管理員 use_for_related_fields 屬性來解決這個問題:
class MyManager(models.Manager): use_for_related_fields = True ...
如果這個屬性在一個模型的 預設 管理器中被設定了(只有在預設管理器中設定才起作用),那麼 Django 就會始終使用這個管理器,否則就會使用 django.db.models.Manager 。
曆史由來
根據屬性的用途,屬性的名稱( use_for_related_fields )可能看上去比較瑣碎。因為原來,這個屬性只用於控制操作管理器的關聯欄位。當這個屬性的概念逐漸明確後,它的名稱沒有改變。這隻是一個初步的情況,在 Django 以後的版本中代碼還會 持續改進 。 在自動管理器執行個體中使用正確的管理器¶
上文 django.contrib.gix 例子中我們已經建議過, use_for_related_fields 功能主要用於需要返回一個自訂 查詢集 子類的管理器。為了讓管理器有效工作,還有一些要牢記的東西: 在這種管理器子類中不要過濾掉任何結果¶
一個原因是一個自動管理器是用於操作其它模型的關聯對象的。在這種情況下, Django 必須可以看見獲得的其它所有模型的對象。因此獲得的任何東西都不要過濾掉。
如果你重載了 get_query_set() 方法並且過濾掉了任何資料庫的行,那麼 Django 會返回不正確的結果。所以不要這樣做。一個過濾 get_query_set() 的結果的管理器不適合用作一個自動管理器。 當定義類時應當設定 use_for_related_fields¶
use_for_related_fields 屬性必須設定在管理器 類 中,而不是一個類的 執行個體 中。上文已經舉了一個正確的例子,下面是一個錯誤的例子:
# 錯誤:不正確的代碼class MyManager(models.Manager): ...# 在 MyManager 的執行個體中設定屬性。 Django 會忽略這個設定。mgr = MyManager()mgr.use_for_related_fields = Trueclass MyModel(models.Model): ... objects = mgr# 錯誤碼結束。</