# 編寫自定義的django-admin命令 #
應用可以通過`manage.py`注冊它們自己的動作。例如,你可能想為你正在發布的Django應用添加一個`manage.py`動作。在本頁文檔中,我們將為教程中的 `polls`應用構建一個自定義的 `closepoll`命令。
要做到這點,只需向該應用添加一個`management/commands`目錄。Django將為該目錄中名字沒有以下劃線開始的每個Python模塊注冊一個`manage.py`命令。例如:
```
polls/
__init__.py
models.py
management/
__init__.py
commands/
__init__.py
_private.py
closepoll.py
tests.py
views.py
```
在Python 2上,請確保`management`和`management/commands`兩個目錄都包含`__init__.py` 文件,否則將檢測不到你的命令。
在這個例子中,`closepoll`命令對任何項目都可使用,只要它們在`INSTALLED_APPS`里包含`polls`應用。
`_private.py`將不可以作為一個管理命令使用。
`closepoll.py`模塊只有一個要求 – 它必須定義一個`Command`類并擴展自`BaseCommand`或其 子類。
> 獨立的腳本
>
> 自定義的管理命令主要用于運行獨立的腳本或者UNIX crontab和Windows周期任務控制面板周期性執行的腳本。
要實現這個命令,需將`polls/management/commands/closepoll.py`編輯成這樣:
```
from django.core.management.base import BaseCommand, CommandError
from polls.models import Poll
class Command(BaseCommand):
help = 'Closes the specified poll for voting'
def add_arguments(self, parser):
parser.add_argument('poll_id', nargs='+', type=int)
def handle(self, *args, **options):
for poll_id in options['poll_id']:
try:
poll = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise CommandError('Poll "%s" does not exist' % poll_id)
poll.opened = False
poll.save()
self.stdout.write('Successfully closed poll "%s"' % poll_id)
```
```
Changed in Django 1.8:
在Django 1.8之前,管理命令基于optparse模塊,位置參數傳遞給*args,可選參數傳遞給**options。現在,管理命令使用argparse解析參數,默認所有的參數都傳遞給**options,除非你命名你的位置參數為args(兼容模式)。對于新的命令,鼓勵你僅僅使用**options。
```
> 注
>
> 當你使用管理命令并希望提供控制臺輸出時,你應該寫到`self.stdout`和`self.stderr`,而不能直接打印到 `stdout`和`stderr`。通過使用這些代理方法,測試你自定義的命令將變得非常容易。還請注意,你不需要在消息的末尾加上一個換行符,它將被自動添加,除非你指定`ending`參數:
>
```
self.stdout.write("Unterminated line", ending='')
```
>
新的自定義命令可以使用`python manage.py closepoll <poll_id>`調用。
`handle()`接收一個或多個`poll_ids`并為他們中的每個設置 `poll.opened`為`False`。如果用戶訪問任何不存在的`polls`,將引發一個`CommandError`。`poll.opened`屬性在教程中并不存在,只是為了這個例子將它添加到`polls.models.Poll`中。
## 接收可選參數 ##
通過接收額外的命令行選項,可以簡單地修改`closepoll`來刪除一個給定的`poll`而不是關閉它。這些自定義的選項可以像下面這樣添加到 `add_arguments()`方法中:
```
class Command(BaseCommand):
def add_arguments(self, parser):
# Positional arguments
parser.add_argument('poll_id', nargs='+', type=int)
# Named (optional) arguments
parser.add_argument('--delete',
action='store_true',
dest='delete',
default=False,
help='Delete poll instead of closing it')
def handle(self, *args, **options):
# ...
if options['delete']:
poll.delete()
# ...
```
```
Changed in Django 1.8:
之前,只支持標準的optparse庫,你必須利用optparse.make_option()擴展命令option_list變量。
```
選項(在我們的例子中為`delete`)在`handle`方法的`options`字典參數中可以訪問到。更多關于`add_argument`用法的信息,請參考`argparse`的Python 文檔。
除了可以添加自定義的命令行選項, 管理命令還可以接收一些默認的選項,例如`--verbosity`和`--traceback`。
## 管理命令和區域設置 ##
默認情況下,`BaseCommand.execute()`方法使轉換失效,因為某些與Django一起的命令完成的任務要求一個與項目無關的語言字符串(例如,面向用戶的內容渲染和數據庫填入)。
```
Changed in Django 1.8:
在之前的版本中,Django強制使用"en-us"區域設置而不是使轉換失效。
```
如果,出于某些原因,你的自定義的管理命令需要使用一個固定的區域設置,你需要在你的`handle()`方法中利用I18N支持代碼提供的函數手工地啟用和停用它:
```
from django.core.management.base import BaseCommand, CommandError
from django.utils import translation
class Command(BaseCommand):
...
can_import_settings = True
def handle(self, *args, **options):
# Activate a fixed locale, e.g. Russian
translation.activate('ru')
# Or you can activate the LANGUAGE_CODE # chosen in the settings:
from django.conf import settings
translation.activate(settings.LANGUAGE_CODE)
# Your command logic here
...
translation.deactivate()
```
另一個需要可能是你的命令只是簡單地應該使用設置中設置的區域設置且Django應該保持不讓它停用。你可以使用`BaseCommand.leave_locale_alone`選項實現這個功能。
雖然上面描述的場景可以工作,但是考慮到系統管理命令對于運行非統一的區域設置通常必須非常小心,所以你可能需要:
+ 確保運行命令時`USE_I18N`設置永遠為`True`(this is a good example of the potential problems stemming from a dynamic runtime environment that Django commands avoid offhand by deactivating translations)。
+ Review the code of your command and the code it calls for behavioral differences when locales are changed and evaluate its impact on predictable behavior of your command.
## 測試 ##
關于如何測試自定義管理命令的信息可以在[測試文檔](http://python.usyiyi.cn/django/topics/testing/tools.html#topics-testing-management-commands)中找到。
## Command 對象 ##
`class BaseCommand`
所有管理命令最終繼承的基類。
如果你想獲得解析命令行參數并在響應中如何調用代碼的所有機制,可以使用這個類;如果你不需要改變這個行為,請考慮使用它的子類。
繼承`BaseCommand`類要求你實現`handle()`方法。
### 屬性 ###
所有的屬性都可以在你派生的類中設置,并在`BaseCommand`的子類中使用。
`BaseCommand.args`
一個字符串,列出命令接收的參數,適合用于幫助信息;例如,接收一個應用名稱列表的命令可以設置它為‘`<app_label app_label ...>`’。
```
Deprecated since version 1.8:
現在,應該在add_arguments()方法中完成,通過調用parser.add_argument()方法。參見上面的closepoll例子。
```
`BaseCommand.can_import_settings`
一個布爾值,指示該命令是否需要導入Django的設置的能力;如果為`True`,`execute()`將在繼續之前驗證這是否可能。默認值為`True`。
`BaseCommand.help`
命令的簡短描述,當用戶運行`python manage.py help <command>`命令時將在幫助信息中打印出來。
`BaseCommand.missing_args_message`
```
New in Django 1.8.
```
如果你的命令定義了必需的位置參數,你可以自定義參數缺失時返回的錯誤信息。默認是由`argparse`輸出的 (“too few arguments”)。
`BaseCommand.option_list`
這是optparse選項列表,將賦值給命令的OptionParser用于解析命令。
```
Deprecated since version 1.8:
現在,你應該覆蓋`add_arguments()`方法來添加命令行接收的自定義參數。參見上面的例子。
```
`BaseCommand.output_transaction`
一個布爾值,指示命令是否輸出SQL語句;如果為`True`,輸出將被自動用`BEGIN;`和`COMMIT;`封裝。默認為`False`。
`BaseCommand.requires_system_checks`
```
New in Django 1.7.
```
一個布爾值;如果為`True`,在執行該命令之前將檢查整個Django項目是否有潛在的問題。如果`requires_system_checks`缺失,則使用`requires_model_validation`的值。如果后者的值也缺失,則使用默認值(`True`)。同時定義`requires_system_checks`和`requires_model_validation`將導致錯誤。
`BaseCommand.requires_model_validation`
```
Deprecated since version 1.7:
被requires_system_checks代替
```
一個布爾值;如果為`True`,將在執行命令之前作安裝的模型的驗證。默認為`True`。若要驗證一個單獨應用的模型而不是全部應用的模型,可以調用在`handle()`中調用`validate()`。
`BaseCommand.leave_locale_alone`
一個布爾值,指示設置中的區域設置在執行命令過程中是否應該保持而不是強制設成‘en-us’。
默認值為`False`。
如果你決定在你自定義的命令中修改該選項的值,請確保你知道你正在做什么。 如果它創建對區域設置敏感的數據庫內容,這種內容不應該包含任何轉換(比如`django.contrib.auth`權限發生的情況),因為將區域設置變成與實際上默認的‘en-us’ 不同可能導致意外的效果。更進一步的細節參見上面的[管理命令和區域設置](http://python.usyiyi.cn/django/howto/custom-management-commands.html#id1)一節。
當`can_import_settings`選項設置為`False`時,該選項不可以也為`False`,因為嘗試設置區域設置需要訪問`settings`。這種情況將產生一個`CommandError`。
## 方法 ##
`BaseCommand`有幾個方法可以被覆蓋,但是只有`handle()`是必須實現的。
> 在子類中實現構造函數
>
> 如果你在`BaseCommand的`子類中實現`__init__`,你必須調用`BaseCommand`的`__init__`:
>
```
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
# ...
```
>
`BaseCommand.add_arguments(parser)`
```
New in Django 1.8.
```
添加解析器參數的入口,以處理傳遞給命令的命令行參數。自定義的命令應該覆蓋這個方法以添加命令行接收的位置參數和可選參數。當直接繼承`BaseCommand`時不需要調用`super()`。
`BaseCommand.get_version()`
返回Django的版本,對于所有內建的Django命令應該都是正確的。用戶提供的命令可以覆蓋這個方法以返回它們自己的版本。
`BaseCommand.execute(*args, **options)`
執行這個命令,如果需要則作系統檢查(通過 `requires_system_checks`屬性控制)。如果該命令引發一個`CommandError`,它將被截斷并打印到標準錯誤輸出。
> 在你的代碼中調用管理命令
>
> 不應該在你的代碼中直接調用`execute()`來執行一個命令。請使用`call_command`。
`BaseCommand.handle(*args, **options)`
命令的真正邏輯。子類必須實現這個方法。
`BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)`
```
New in Django 1.7.
```
利用系統的檢測框架檢測全部Django項目的潛在問題。嚴重的問題將引發`CommandError`;警告會輸出到標準錯誤輸出;次要的通知會輸出到標準輸出。
如果`app_configs`和`tags`都為`None`,將進行所有的系統檢查。`tags`可以是一個要檢查的標簽列表,比如`compatibility`或`models`。
`BaseCommand.validate(app=None, display_num_errors=False)`
```
Deprecated since version 1.7:
被check命令代替
```
如果`app`為`None`,那么將檢查安裝的所有應用的錯誤。
### BaseCommand 的子類 ###
`class AppCommand`
這個管理命令接收一個或多個安裝的應用標簽作為參數,并對它們每一個都做一些動作。
子類不用實現`handle()`,但必須實現`handle_app_config()`,它將會為每個應用調用一次。
`AppCommand.handle_app_config(app_config, **options)`
對`app_config`完成命令行的動作,其中`app_config`是`AppConfig`的實例,對應于在命令行上給出的應用標簽。
> Changed in Django 1.7:
>
> 以前,AppCommand子類必須實現`handle_app(app, **options)`,其中`app`是一個模型模塊。新的API可以不需要模型模塊來處理應用。遷移的最快的方法如下:
>
```
def handle_app_config(app_config, **options):
if app_config.models_module is None:
return # Or raise an exception.
app = app_config.models_module
# Copy the implementation of handle_app(app_config, **options) here.
```
>
> 然而,你可以通過直接使用`app_config`的屬性來簡化實現。
`class LabelCommand`
這個管理命令接收命令行上的一個或多個參數(標簽),并對它們每一個都做一些動作。
子類不用實現`handle()`,但必須實現`handle_label()`,它將會為每個標簽調用一次。
`LabelCommand.handle_label(label, **options)`
對`label`完成命令行的動作,`label`是命令行給出的字符串。
`class NoArgsCommand`
```
Deprecated since version 1.8:
使用BaseCommand代替,它默認也不需要參數。
```
這個命令不接收命令行上的參數。
子類不需要實現`handle()`,但必須實現`handle_noargs()`;`handle()`本身已經被覆蓋以保證不會有參數傳遞給命令。
`NoArgsCommand.handle_noargs(**options)`
完成這個命令的動作
### Command 的異常 ###
`class CommandError`
異常類,表示執行一個管理命令時出現問題。
如果這個異常是在執行一個來自命令行控制臺的管理命令時引發,它將被捕獲并轉換成一個友好的錯誤信息到合適的輸出流(例如,標準錯誤輸出);因此,引發這個異常(并帶有一個合理的錯誤描述)是首選的方式來指示在執行一個命令時某些東西出現錯誤。
如果管理命令從代碼中通過call_command調用,那么需要時捕獲這個異常由你決定。
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Adding custom commands](https://docs.djangoproject.com/en/1.8/howto/custom-management-commands/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第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格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架