鍍金池/ 教程/ Python/ 編寫自定義的django-admin命令
點(diǎn)擊劫持保護(hù)
安全問題歸檔
Model 類參考
將遺留數(shù)據(jù)庫整合到Django
關(guān)聯(lián)對象參考
內(nèi)建基于類的視圖的API
聚合
Django 中的用戶認(rèn)證
django.contrib.humanize
Django管理文檔生成器
分頁
使用Django輸出CSV
加密簽名
文件儲存API
安全
Django中的測試
國際化和本地化
為Django編寫首個(gè)補(bǔ)丁
條件表達(dá)式
日志
模型元選項(xiàng)
部署靜態(tài)文件
執(zhí)行查詢
使用Django認(rèn)證系統(tǒng)
基于類的視圖
中間件
編寫自定義的django-admin命令
Django 的設(shè)置
格式本地化
數(shù)據(jù)庫訪問優(yōu)化
錯(cuò)誤報(bào)告
基于類的內(nèi)建通用視圖
編寫自定義存儲系統(tǒng)
編寫你的第一個(gè) Django 程序 第3部分
編寫數(shù)據(jù)庫遷移
使用表單
編寫你的第一個(gè) Django 程序 第2部分
編寫你的第一個(gè) Django 程序 第1部分
如何使用會話
系統(tǒng)檢查框架
新手入門
信號
編寫視圖
如何使用WSGI 部署
編寫你的第一個(gè)Django應(yīng)用,第6部分
常見的網(wǎng)站應(yīng)用工具
Widgets
內(nèi)建的視圖
模型實(shí)例參考
視圖層
Django中的密碼管理
高級教程:如何編寫可重用的應(yīng)用
國際化和本地化
"本地特色"附加功能
TemplateResponse 和 SimpleTemplateResponse
模式編輯器
文件上傳
快速安裝指南
部署 Django
表單 API
表單素材 ( <code>Media</code> 類)
管理文件
其它核心功能
查找 API 參考
表單
Admin
數(shù)據(jù)庫函數(shù)
自定義查找
使用基于類的視圖處理表單
管理操作
開發(fā)過程
編寫你的第一個(gè)Django應(yīng)用,第5部分
進(jìn)行原始的sql查詢
模型層
多數(shù)據(jù)庫
編寫你的第一個(gè) Django 程序 第4部分
Django安全
Django 初探
Django異常
重定向應(yīng)用
按需內(nèi)容處理
管理器
視圖裝飾器
驗(yàn)證器
使用Django輸出PDF
File對象
Django 的快捷函數(shù)
基于類的通用視圖 —— 索引
為模型提供初始數(shù)據(jù)
模板層
URL調(diào)度器
中間件
模型

編寫自定義的django-admin命令

應(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上,請確保managementmanagement/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.stdoutself.stderr,而不能直接打印到 stdoutstderr。通過使用這些代理方法,測試你自定義的命令將變得非常容易。還請注意,你不需要在消息的末尾加上一個(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.openedFalse。如果用戶訪問任何不存在的polls,將引發(fā)一個(gè)CommandError。poll.opened屬性在教程中并不存在,只是為了這個(gè)例子將它添加到polls.models.Poll中。

接收可選參數(shù)

通過接收額外的命令行選項(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。

管理命令和區(qū)域設(shè)置

默認(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è)置通常必須非常小心,所以你可能需要:

  • 確保運(yùn)行命令時(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)。
  • 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.

測試

關(guān)于如何測試自定義管理命令的信息可以在測試文檔中找到。

Command 對象

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_checksrequires_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_configstags都為None,將進(jìn)行所有的系統(tǒng)檢查。tags可以是一個(gè)要檢查的標(biāo)簽列表,比如compatibilitymodels。

BaseCommand.validate(app=None, display_num_errors=False)

Deprecated since version 1.7:

被check命令代替

如果appNone,那么將檢查安裝的所有應(yīng)用的錯(cuò)誤。

BaseCommand 的子類

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_configAppConfig的實(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è)命令的動作

Command 的異常

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。