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