應(yīng)用可以通過manage.py注冊它們自己的動作。例如,你可能想為你正在發(fā)布的Django應(yīng)用添加一個manage.py動作。在本頁文檔中,我們將為教程中的 polls應(yīng)用構(gòu)建一個自定義的 closepoll命令。
要做到這點,只需向該應(yīng)用添加一個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應(yīng)用。
_private.py將不可以作為一個管理命令使用。
closepoll.py模塊只有一個要求 – 它必須定義一個Command類并擴展自BaseCommand或其 子類。
獨立的腳本
自定義的管理命令主要用于運行獨立的腳本或者UNIX crontab和Windows周期任務(wù)控制面板周期性執(zhí)行的腳本。
要實現(xiàn)這個命令,需將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模塊,位置參數(shù)傳遞給*args,可選參數(shù)傳遞給**options。現(xiàn)在,管理命令使用argparse解析參數(shù),默認(rèn)所有的參數(shù)都傳遞給**options,除非你命名你的位置參數(shù)為args(兼容模式)。對于新的命令,鼓勵你僅僅使用**options。
注
當(dāng)你使用管理命令并希望提供控制臺輸出時,你應(yīng)該寫到
self.stdout和self.stderr,而不能直接打印到stdout和stderr。通過使用這些代理方法,測試你自定義的命令將變得非常容易。還請注意,你不需要在消息的末尾加上一個換行符,它將被自動添加,除非你指定ending參數(shù):self.stdout.write("Unterminated line", ending='')
新的自定義命令可以使用python manage.py closepoll <poll_id>調(diào)用。
handle()接收一個或多個poll_ids并為他們中的每個設(shè)置 poll.opened為False。如果用戶訪問任何不存在的polls,將引發(fā)一個CommandError。poll.opened屬性在教程中并不存在,只是為了這個例子將它添加到polls.models.Poll中。
通過接收額外的命令行選項,可以簡單地修改closepoll來刪除一個給定的poll而不是關(guān)閉它。這些自定義的選項可以像下面這樣添加到 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:
之前,只支持標(biāo)準(zhǔn)的optparse庫,你必須利用optparse.make_option()擴展命令option_list變量。
選項(在我們的例子中為delete)在handle方法的options字典參數(shù)中可以訪問到。更多關(guān)于add_argument用法的信息,請參考argparse的Python 文檔。
除了可以添加自定義的命令行選項, 管理命令還可以接收一些默認(rèn)的選項,例如--verbosity和--traceback。
默認(rèn)情況下,BaseCommand.execute()方法使轉(zhuǎn)換失效,因為某些與Django一起的命令完成的任務(wù)要求一個與項目無關(guān)的語言字符串(例如,面向用戶的內(nèi)容渲染和數(shù)據(jù)庫填入)。
Changed in Django 1.8:
在之前的版本中,Django強制使用"en-us"區(qū)域設(shè)置而不是使轉(zhuǎn)換失效。
如果,出于某些原因,你的自定義的管理命令需要使用一個固定的區(qū)域設(shè)置,你需要在你的handle()方法中利用I18N支持代碼提供的函數(shù)手工地啟用和停用它:
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()
另一個需要可能是你的命令只是簡單地應(yīng)該使用設(shè)置中設(shè)置的區(qū)域設(shè)置且Django應(yīng)該保持不讓它停用。你可以使用BaseCommand.leave_locale_alone選項實現(xiàn)這個功能。
雖然上面描述的場景可以工作,但是考慮到系統(tǒng)管理命令對于運行非統(tǒng)一的區(qū)域設(shè)置通常必須非常小心,所以你可能需要:
USE_I18N設(shè)置永遠(yuǎn)為True(this is a good example of the potential problems stemming from a dynamic runtime environment that Django commands avoid offhand by deactivating translations)。關(guān)于如何測試自定義管理命令的信息可以在測試文檔中找到。
class BaseCommand
所有管理命令最終繼承的基類。
如果你想獲得解析命令行參數(shù)并在響應(yīng)中如何調(diào)用代碼的所有機制,可以使用這個類;如果你不需要改變這個行為,請考慮使用它的子類。
繼承BaseCommand類要求你實現(xiàn)handle()方法。
所有的屬性都可以在你派生的類中設(shè)置,并在BaseCommand的子類中使用。
BaseCommand.args
一個字符串,列出命令接收的參數(shù),適合用于幫助信息;例如,接收一個應(yīng)用名稱列表的命令可以設(shè)置它為‘<app_label app_label ...>’。
Deprecated since version 1.8:
現(xiàn)在,應(yīng)該在add_arguments()方法中完成,通過調(diào)用parser.add_argument()方法。參見上面的closepoll例子。
BaseCommand.can_import_settings
一個布爾值,指示該命令是否需要導(dǎo)入Django的設(shè)置的能力;如果為True,execute()將在繼續(xù)之前驗證這是否可能。默認(rèn)值為True。
BaseCommand.help
命令的簡短描述,當(dāng)用戶運行python manage.py help <command>命令時將在幫助信息中打印出來。
BaseCommand.missing_args_message
New in Django 1.8.
如果你的命令定義了必需的位置參數(shù),你可以自定義參數(shù)缺失時返回的錯誤信息。默認(rèn)是由argparse輸出的 (“too few arguments”)。
BaseCommand.option_list
這是optparse選項列表,將賦值給命令的OptionParser用于解析命令。
Deprecated since version 1.8:
現(xiàn)在,你應(yīng)該覆蓋`add_arguments()`方法來添加命令行接收的自定義參數(shù)。參見上面的例子。
BaseCommand.output_transaction
一個布爾值,指示命令是否輸出SQL語句;如果為True,輸出將被自動用BEGIN;和COMMIT;封裝。默認(rèn)為False。
BaseCommand.requires_system_checks
New in Django 1.7.
一個布爾值;如果為True,在執(zhí)行該命令之前將檢查整個Django項目是否有潛在的問題。如果requires_system_checks缺失,則使用requires_model_validation的值。如果后者的值也缺失,則使用默認(rèn)值(True)。同時定義requires_system_checks和requires_model_validation將導(dǎo)致錯誤。
BaseCommand.requires_model_validation
Deprecated since version 1.7:
被requires_system_checks代替
一個布爾值;如果為True,將在執(zhí)行命令之前作安裝的模型的驗證。默認(rèn)為True。若要驗證一個單獨應(yīng)用的模型而不是全部應(yīng)用的模型,可以調(diào)用在handle()中調(diào)用validate()。
BaseCommand.leave_locale_alone
一個布爾值,指示設(shè)置中的區(qū)域設(shè)置在執(zhí)行命令過程中是否應(yīng)該保持而不是強制設(shè)成‘en-us’。
默認(rèn)值為False。
如果你決定在你自定義的命令中修改該選項的值,請確保你知道你正在做什么。 如果它創(chuàng)建對區(qū)域設(shè)置敏感的數(shù)據(jù)庫內(nèi)容,這種內(nèi)容不應(yīng)該包含任何轉(zhuǎn)換(比如django.contrib.auth權(quán)限發(fā)生的情況),因為將區(qū)域設(shè)置變成與實際上默認(rèn)的‘en-us’ 不同可能導(dǎo)致意外的效果。更進一步的細(xì)節(jié)參見上面的管理命令和區(qū)域設(shè)置一節(jié)。
當(dāng)can_import_settings選項設(shè)置為False時,該選項不可以也為False,因為嘗試設(shè)置區(qū)域設(shè)置需要訪問settings。這種情況將產(chǎn)生一個CommandError。
BaseCommand有幾個方法可以被覆蓋,但是只有handle()是必須實現(xiàn)的。
在子類中實現(xiàn)構(gòu)造函數(shù)
如果你在
BaseCommand的子類中實現(xiàn)__init__,你必須調(diào)用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.
添加解析器參數(shù)的入口,以處理傳遞給命令的命令行參數(shù)。自定義的命令應(yīng)該覆蓋這個方法以添加命令行接收的位置參數(shù)和可選參數(shù)。當(dāng)直接繼承BaseCommand時不需要調(diào)用super()。
BaseCommand.get_version()
返回Django的版本,對于所有內(nèi)建的Django命令應(yīng)該都是正確的。用戶提供的命令可以覆蓋這個方法以返回它們自己的版本。
BaseCommand.execute(*args, **options)
執(zhí)行這個命令,如果需要則作系統(tǒng)檢查(通過 requires_system_checks屬性控制)。如果該命令引發(fā)一個CommandError,它將被截斷并打印到標(biāo)準(zhǔn)錯誤輸出。
在你的代碼中調(diào)用管理命令
不應(yīng)該在你的代碼中直接調(diào)用
execute()來執(zhí)行一個命令。請使用call_command。
BaseCommand.handle(*args, **options)
命令的真正邏輯。子類必須實現(xiàn)這個方法。
BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)
New in Django 1.7.
利用系統(tǒng)的檢測框架檢測全部Django項目的潛在問題。嚴(yán)重的問題將引發(fā)CommandError;警告會輸出到標(biāo)準(zhǔn)錯誤輸出;次要的通知會輸出到標(biāo)準(zhǔn)輸出。
如果app_configs和tags都為None,將進行所有的系統(tǒng)檢查。tags可以是一個要檢查的標(biāo)簽列表,比如compatibility或models。
BaseCommand.validate(app=None, display_num_errors=False)
Deprecated since version 1.7:
被check命令代替
如果app為None,那么將檢查安裝的所有應(yīng)用的錯誤。
class AppCommand
這個管理命令接收一個或多個安裝的應(yīng)用標(biāo)簽作為參數(shù),并對它們每一個都做一些動作。
子類不用實現(xiàn)handle(),但必須實現(xiàn)handle_app_config(),它將會為每個應(yīng)用調(diào)用一次。
AppCommand.handle_app_config(app_config, **options)
對app_config完成命令行的動作,其中app_config是AppConfig的實例,對應(yīng)于在命令行上給出的應(yīng)用標(biāo)簽。
Changed in Django 1.7:
以前,AppCommand子類必須實現(xiàn)
handle_app(app, **options),其中app是一個模型模塊。新的API可以不需要模型模塊來處理應(yīng)用。遷移的最快的方法如下: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的屬性來簡化實現(xiàn)。
class LabelCommand
這個管理命令接收命令行上的一個或多個參數(shù)(標(biāo)簽),并對它們每一個都做一些動作。
子類不用實現(xiàn)handle(),但必須實現(xiàn)handle_label(),它將會為每個標(biāo)簽調(diào)用一次。
LabelCommand.handle_label(label, **options)
對label完成命令行的動作,label是命令行給出的字符串。
class NoArgsCommand
Deprecated since version 1.8:
使用BaseCommand代替,它默認(rèn)也不需要參數(shù)。
這個命令不接收命令行上的參數(shù)。
子類不需要實現(xiàn)handle(),但必須實現(xiàn)handle_noargs();handle()本身已經(jīng)被覆蓋以保證不會有參數(shù)傳遞給命令。
NoArgsCommand.handle_noargs(**options)
完成這個命令的動作
class CommandError
異常類,表示執(zhí)行一個管理命令時出現(xiàn)問題。
如果這個異常是在執(zhí)行一個來自命令行控制臺的管理命令時引發(fā),它將被捕獲并轉(zhuǎn)換成一個友好的錯誤信息到合適的輸出流(例如,標(biāo)準(zhǔn)錯誤輸出);因此,引發(fā)這個異常(并帶有一個合理的錯誤描述)是首選的方式來指示在執(zhí)行一個命令時某些東西出現(xiàn)錯誤。
如果管理命令從代碼中通過call_command調(diào)用,那么需要時捕獲這個異常由你決定。
譯者:Django 文檔協(xié)作翻譯小組,原文:Adding custom commands。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。