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