<!--
譯者:Github@wizardforcel
-->
# 管理器 #
**class Manager**
管理器是一個接口,數據庫查詢操作通過它提供給django的模型。django應用的每個模型至少擁有一個 管理器。
管理器類的工作方式在 執行查詢文檔中闡述,而這篇文檔涉及了自定義管理器行為的模型選項。
## 管理器的名字 ##
通常,django為每個模型類添加一個名為objects的管理器。然而,如果你想將objects用于字段名稱,或者你想使用其它名稱而不是objects訪問管理器,你可以在每個模型類中重命名它。在模型中定義一個值為models.Manager()的屬性,來重命名管理器。例如:
```
from django.db import models
class Person(models.Model):
#...
people = models.Manager()
```
使用例子中的模型, Person.objects會拋出AttributeError異常,而Person.people.all()會返回一個包含所有Person對象的列表。
## 自定義管理器 ##
在一個特定的模型中,你可以通過繼承管理器類來構建一個自定義的管理器,以及實例化你的自定義管理器。
你有兩個原因可能會自己定義管理器:向器類中添加額外的方法,或者修改管理器最初返回的查詢集。
### 添加額外的管理器方法 ###
為你的模型添加表級(table-level)功能時,采用添加額外的管理器方法是更好的處理方式。如果要添加行級功能--就是說該功能只對某個模型的實例對象起作用。在這種情況下,使用 模型方法 比使用自定義的管理器方法要更好。)
自定義的管理器 方法可以返回你想要的任何數據,而不只是查詢集。
例如,下面這個自定義的 管理器提供了一個 with_counts() 方法,它返回所有 OpinionPoll 對象的列表,而且列表中的每個對象都多了一個名為 num_responses的屬性,這個屬性保存一個聚合查詢(COUNT*)的結果:
```
from django.db import models
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 p.id, p.question, p.poll_date
ORDER BY p.poll_date 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_list
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll)
person_name = models.CharField(max_length=50)
response = models.TextField()
```
在這個例子中,你已經可以使用 OpinionPoll.objects.with_counts() 得到所有含有 num_responses屬性的 OpinionPoll對象。
這個例子要注意的一點是: 管理器方法可以訪問 self.model來得到它所用到的模型類。
### 修改管理器初始的查詢集 ###
管理器自帶的 查詢集返回系統中所有的對象。例如,使用下面這個模型:
```
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
```
... Book.objects.all() 語句將返回數據庫中所有的 Book 對象。
你可以通過重寫 Manager.get_queryset() 的方法來覆蓋 管理器自帶的 查詢集。get_queryset() 會根據你所需要的屬性返回 查詢集。
例如,下面的模型有兩個 管理器,一個返回所有的對象,另一個則只返回作者是 Roald Dahl 的對象:
```
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
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 Dahl 的圖書。
由于 get_queryset() 返回的是一個 查詢集 對象,所以你仍可以對它使用 filter(), exclude()和其他 查詢集的方法。所以下面這些例子都是可用的:
```
Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()
```
這個例子還展示了另外一個很有意思的技巧:在同一個模型中使用多個管理器。你可以隨你所意在一個模型里面添加多個 Manager() 實例。下面就用很簡單的方法,給模型添加通用過濾器:
例如:
```
class AuthorManager(models.Manager):
def get_queryset(self):
return super(AuthorManager, self).get_queryset().filter(role='A')
class EditorManager(models.Manager):
def get_queryset(self):
return super(EditorManager, self).get_queryset().filter(role='E')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
people = models.Manager()
authors = AuthorManager()
editors = EditorManager()
```
在這個例子中,你使用 Person.authors.all(), Person.editors.all(),以及 Person.people.all(), 都會得到和名稱相符的結果。
#### 默認管理器 ####
如果你使用了自定義 管理器對象,要注意 Django 中的第一個 管理器 (按照模型中出現的順序而定) 擁有特殊的地位。Django 會將模型中定義的管理器解釋為默認的 管理器,并且 Django 中的一部分應用(包括數據備份)會使用默認的管理器,除了前面那個模型。因此,要決定默認的管理器時,要小心謹慎,仔細考量,這樣才能避免重寫 get_queryset() 導致無法正確地獲得數據。
#### 使用管理器訪問關聯對象 ####
默認情況下,在訪問相關對象時(例如choice.poll),Django 并不使用相關對象的默認管理器,而是使用一個"樸素"管理器類的實例來訪問。這是因為 Django 要能從關聯對象中獲得數據,但這些數據有可能被默認管理器過濾掉,或是無法進行訪問。
如果普通的樸素管理器類(django.db.models.Manager)并不適用于你的應用,那么你可以通過在管理器類中設置 use_for_related_fields ,強制 Django 在你的模型中使用默認的管理器。這部分內容在 下面有 詳細介紹。
### 調用自定義的查詢集 ###
雖然大多數標準查詢集的方法可以從管理器中直接訪問到,但是這是一個例子,訪問了定義在自定義 查詢集上的額外方法,如果你也在管理器上面實現了它們:
```
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.filter(role='E')
class PersonManager(models.Manager):
def get_queryset(self):
return PersonQuerySet(self.model, using=self._db)
def authors(self):
return self.get_queryset().authors()
def editors(self):
return self.get_queryset().editors()
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
people = PersonManager()
```
這個例子展示了如何直接從管理器 Person.people調用authors() 和 editors()。
### 創建管理器 ###
** django 1.7 中新增 **
對于上面的例子,同一個方法需要在查詢集 和 管理器上創建兩份副本,作為替代,QuerySet.as_manager()可以創建一個管理器的實例,它擁有自定義查詢集的方法:
```
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
```
通過QuerySet.as_manager()創建的管理器 實例,實際上等價于上面例子中的PersonManager。
并不是每個查詢集的方法都在管理器層面上有意義。比如 QuerySet.delete(),我們有意防止它復制到管理器 中。
方法按照以下規則進行復制:
+ 公共方法默認被復制。
+ 私有方法(前面帶一個下劃線)默認不被復制。
+ 帶queryset_only 屬性,并且值為False的方法總是被復制。
+ 帶 queryset_only 屬性,并且值為True 的方法不會被復制。
例如:
```
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def public_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
# Available only on QuerySet.
def opted_out_public_method(self):
return
opted_out_public_method.queryset_only = True
# Available on both Manager and QuerySet.
def _opted_in_private_method(self):
return
_opted_in_private_method.queryset_only = False
```
#### from_queryset ####
```
classmethod from_queryset(queryset_class)
```
在進一步的使用中,你可能想創建一個自定義管理器和一個自定義查詢集。你可以調用Manager.from_queryset(),它會返回管理器的一個子類,帶有自定義查詢集所有方法的副本:
```
class BaseManager(models.Manager):
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
def manager_and_queryset_method(self):
return
class MyModel(models.Model):
objects = BaseManager.from_queryset(CustomQueryset)()
```
你也可以在一個變量中儲存生成的類:
```
CustomManager = BaseManager.from_queryset(CustomQueryset)
class MyModel(models.Model):
objects = CustomManager()
```
### 自定義管理器和模型繼承 ###
類繼承和模型管理器兩者之間配合得并不是很好。 管理器一般只對其定義所在的類起作用,在子類中對其繼承絕對不是一個好主意。 而且,因為第一個 管理器會被 Djange 聲明為默認的管理器,所以對默認的管理器 進行控制是非常必要的。下面就是 Django 如何處理自定義管理器和模型繼承(model inheritance)的:
+ 定義在非抽象基類中的管理器是 不會 被子類繼承的。如果你想從一個非抽象基類中重用管理器,只能在子類中重定義管理器。 這是因為這種管理器與定義它的模型 綁定得非常緊密,所以繼承它們經常會導致異常的結果(特別是默認管理器運行的時候)。 因此,它們不應繼承給子類。
+ 定義在抽象基類中的管理器總是被子類繼續的,是按 Python 的命名解析順序解析的(首先是子類中的命名覆蓋所有的,然后是第一個父類的,以此類推)。 抽象類用來提取子類中的公共信息和行為,定義公共管理器也是公共信息的一部分。
+ 如果類當中顯示定義了默認管理器,Django 就會以此做為默認管理器;否則就會從第一個抽象基類中繼承默認管理器; 如果沒有顯式聲明默認管理器,那么 Django 就會自動添加默認管理器。
如果你想在一組模型上安裝一系列自定義管理器,上面提到的這些規則就已經為你的實現提供了必要的靈活性。你可以繼承一個抽象基類,但仍要自定義默認的管理器。例如,假設你的基類是這樣的:
```
class AbstractBase(models.Model):
# ...
objects = CustomManager()
class Meta:
abstract = True
```
如果你在基類中沒有定義管理器,直接使用上面的代碼,默認管理器就是從基類中繼承的 objects:
```
class ChildA(AbstractBase):
# ...
# This class has CustomManager as the default manager.
pass
```
如果你想從 AbstractBase繼承,卻又想提供另一個默認管理器,那么你可以在子類中定義默認管理器:
```
class ChildB(AbstractBase):
# ...
# An explicit default manager.
default_manager = OtherManager()
```
在這個例子中, default_manager就是默認的 管理器。從基類中繼承的 objects 管理器仍是可用的。只不過它不再是默認管理器罷了。
最后再舉個例子,假設你想在子類中再添加一個額外的管理器,但是很想使用從 AbstractBase繼承的管理器做為默認管理器。那么,你不在直接在子類中添加新的管理器,否則就會覆蓋掉默認管理器,而且你必須對派生自這個基類的所有子類都顯示指定管理器。 解決辦法就是在另一個基類中添加新的管理器,然后繼承時將其放在默認管理器所在的基類 之后。例如:
```
class ExtraManager(models.Model):
extra_manager = OtherManager()
class Meta:
abstract = True
class ChildC(AbstractBase, ExtraManager):
# ...
# Default manager is CustomManager, but OtherManager is
# also available via the "extra_manager" attribute.
pass
```
注意在抽象模型上面定義一個自定義管理器的時候,不能調用任何使用這個抽象模型的方法。就像:
```
ClassA.objects.do_something()
```
是可以的,但是:
```
AbstractBase.objects.do_something()
```
會拋出一個異常。這是因為,管理器被設計用來封裝對象集合管理的邏輯。由于抽象的對象中并沒有一個集合,管理它們是毫無意義的。如果你寫了應用在抽象模型上的功能,你應該把功能放到抽象模型的靜態方法,或者類的方法中。
### 實現上的注意事項 ###
無論你向自定義管理器中添加了什么功能,都必須可以得到 管理器實例的一個淺表副本:例如,下面的代碼必須正常運行:
```
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
```
Django 在一些查詢中會創建管理器的淺表副本;如果你的管理器不能被復制,查詢就會失敗。
這對于大多數自定義管理器不是什么大問題。如果你只是添加一些簡單的方法到你的管理器中,不太可能會把你的管理器實例變為不可復制的。但是,如果你覆蓋了\_\_getattr\_\_,或者其它管理器中控制對象狀態的私有方法,你應該確保不會影響到管理器的復制。
## 控制自動管理器的類型 ##
這篇文檔已經提到了Django創建管理器類的一些位置:默認管理器和用于訪問關聯對象的“樸素” 管理器。在 Django 的實現中也有很多地方用到了臨時的樸素管理器。 正常情況下,django.db.models.Manager 類的實例會自動創建管理器。
在整個這一節中,我們將那種由 Django 為你創建的管理器稱之為 "自動管理器",既有因為沒有管理器而被 Django 自動添加的默認管理器, 也包括在訪問關聯模型時使用的臨時管理器。
有時,默認管理器也并非是自動管理器。 一個例子就是 Django 自帶的django.contrib.gis 應用,所有 gis模型都必須使用一個特殊的管理器類(GeoManager),因為它們需要運行特殊的查詢集(GeoQuerySet)與數據庫進行交互。這表明無論自動管理器是否被創建,那些要使用特殊的管理器的模型仍要使用這個特殊的管理器類。
Django 為自定義管理器的開發者提供了一種方式:無論開發的管理器類是不是默認的管理器,它都應該可以用做自動管理器。 可以通過在管理器類中設置 use_for_related_fields 屬性來做到這點:
```
class MyManager(models.Manager):
use_for_related_fields = True
# ...
```
如果在模型中的默認 管理器(在這些情況中僅考慮默認管理器)中設置了這個屬性,那么無論它是否需要被自動創建,Django 都會自動使用它。否則 Django 就會使用 django.db.models.Manager.
> 歷史回顧
>
> 從它使用目的來看,這個屬性的名稱(use_for_related_fields)好象有點古怪。原本,這個屬性僅僅是用來控制訪問關聯字段的管理器的類型,這就是它名字的由來。 后來它的作用更加拓寬了,但是名稱一直未變。 因為要保證現在的代碼在 Django 以后的版本中仍可以正常工作(continue to work),這就是它名稱不變的原因。
### 在自動管理器實例中編寫正確的管理器 ###
在上面的django.contrib.gis 已經提到了, use_for_related_fields這個特性是在需要返回一個自定義查詢集子類的管理器中使用的。要在你的管理器中提供這個功能,要注意以下幾點。
#### 不要在這種類型的管理器子類中過濾掉任何結果 ####
一個原因是自動管理器是用來訪問關聯模型 的對象。 在這種情況下,Django 必須要能看到相關模型的所有對象,所以才能根據關聯關系得到任何數據 。
如果你重寫了 get_queryset() 方法并且過濾掉了一些行數據,Django 將返回不正確的結果。不要這么做! 在 get_queryset()方法中過濾掉數據,會使得它所在的管理器不適于用做自動管理器。
#### 設置 use_for_related_fields ####
use_for_related_fields屬性必須在管理器類中設置,而不是在類的 實例中設置。上面已經有例子展示如何正確地設置,下面這個例子就是一個錯誤的示范:
```
# BAD: Incorrect code
class MyManager(models.Manager):
# ...
pass
# Sets the attribute on an instance of MyManager. Django will
# ignore this setting.
mgr = MyManager()
mgr.use_for_related_fields = True
class MyModel(models.Model):
# ...
objects = mgr
# End of incorrect code.
```
你也不應該在模型中使用這個屬性之后,在類上改變它。這是因為在模型類被創建時,這個屬性值馬上就會被處理,而且隨后不會再讀取這個屬性值。 這節的第一個例子就是在第一次定義的時候在管理器上設置use_for_related_fields屬性,所有的代碼就工作得很好。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架