官方文档

  • 安装
    • pip install flask-script
  • 初始化
    • 使用app构建manager对象
    • 使用manager启动程序
  • 使用
    • shell
      • 进入flask的环境的shell
    • runserver
      • 启动服务器
      • 可以指定参数
        • -r 自动重新加载
        • -d 调试模式
        • -p PORT端口
        • -h HOST主机
  • 使用示例
    • 定义脚本命令后,可以在命令行运行
      • python manage.py hello
    • 运行结果为:hello
# manage.py
from flask_script import Manager
from myapp import app

manager = Manager(app)

@manager.command
def hello():
    print "hello"

if __name__ == "__main__":
    manager.run()

创建和运行命令

第一步是创建一个Python模块来运行脚本命令。你可以随便叫它什么,在例子中我们叫它management.py。

不必将所有命令都放在同一个文件中;例如,在一个包含许多命令的大型项目中,可能希望将它们分割为包含相关命令的多个文件。

在Manager .py文件中,必须创建一个Manager实例。Manager类跟踪所有命令,并处理如何从命令行调用它们:

from flask_script import Manager
app = Flask(__name__)
# configure your app
manager = Manager(app)
if __name__ == "__main__":
    manager.run()

调用Manager .run()使Manager实例准备从命令行接收输入。

Manager需要一个参数,一个Flask实例。如果您想使用工厂模式,这也可以是一个函数或其他可调用的函数,返回一个Flask实例。

下一步是创建和添加命令。创建命令有三种方法:

  • 生成Command类的子类
  • 使用@command装饰器
  • 使用@option装饰器

举一个非常简单的例子,我们想创建一个hello命令,它只输出“hello world”。它不需要任何参数,所以很简单:

from flask_script import Command

class Hello(Command):
    "prints hello world"

    def run(self):
        print "hello world"

现在这个命令需要添加到我们的Manager实例中,就像上面创建的一样:

manager.add_command('hello', Hello())

当然,这需要在manager.run之前调用。现在在我们的命令行中:

python manage.py hello
> hello world

还可以将dict中的命令实例传递给manager.run()

manager.run({'hello' : Hello()})

Command类必须定义一个run方法。位置参数和可选参数取决于传递给命令的命令行参数(参见下面)。

要获得可用命令及其描述的列表,只需在没有命令的情况下运行:

python manage.py

要获取特定命令的帮助文本:

python manage.py runserver -?

这将打印usage加上命令的docstring。

第一个方法可能是最灵活的,但也是最冗长的。对于更简单的命令,可以使用@command decorator,它属于Manager实例:

@manager.command
def hello():
    "Just say hello"
    print "hello"

用这种方法创建的命令与用命令类创建的命令运行方式完全相同:

与命令类一样,当运行-?或者——帮助选项:

python manage.py -?
> Just say hello

最后,当你想对命令进行更复杂的控制时,可以使用同样属于Manager的@option decorator:

@manager.option('-n', '--name', help='Your name')
def hello(name):
    print "hello", name

添加参数到命令

大多数命令接受在命令行中传递的许多命名或位置参数。

以上面的例子为例,我们不只是打印“hello world”,而是希望能够打印一些任意的名称,比如:

python manage.py hello --name=Joe
hello Joe

或者

python manage.py hello -n Joe

为了实现这一点,可以使用Command类的option_list属性:

from flask_script import Command, Manager, Option

class Hello(Command):

    option_list = (
        Option('--name', '-n', dest='name'),
    )

    def run(self, name):
        print "hello %s" % name

位置参数和可选参数存储为选项实例—有关详细信息,请参阅下面的API。

或者,您可以为命令类定义get_options方法。这是有用的,如果你想能够返回选项在运行时基于例如每个实例的属性:

class Hello(Command):

    def __init__(self, default_name='Joe'):
        self.default_name=default_name

    def get_options(self):
        return [
            Option('-n', '--name', dest='name', default=self.default_name),
        ]

    def run(self, name):
        print "hello",  name

如果您使用@command装饰器,就会简单得多——选项会自动从函数参数中提取。这是一个位置参数的例子:

@manager.command
def hello(name):
    print "hello", name

然后在命令行上调用它,如下所示:

> python manage.py hello Joe
hello Joe

或者你可以选择参数:

@manager.command
def hello(name="Fred")
    print "hello", name
> python manage.py hello --name=Joe
hello Joe

或者

> python manage.py hello -n Joe
hello Joe

短形式-n由参数的第一个字母组成,所以“name”是>“-n”。因此,可选参数变量名最好以不同的字母开头。

还要注意,如果你的可选参数是布尔值,例如:

@manager.command
def verify(verified=False):
    """
    Checks if verified
    """
    print "VERIFIED?", "YES" if verified else "NO"

你可以这样调用它

> python manage.py verify
VERIFIED? NO

> python manage.py verify -v
VERIFIED? YES

> python manage.py verify --verified
VERIFIED? YES

@command装饰器适用于简单的操作,但通常需要灵活性。对于更复杂的选项,最好使用@option decorator:

@manager.option('-n', '--name', dest='name', default='joe')
def hello(name):
    print "hello", name

你可以添加你想要的选项:

@manager.option('-n', '--name', dest='name', default='joe')
@manager.option('-u', '--url', dest='url', default=None)
def hello(name, url):
    if url is None:
        print "hello", name
    else:
        print "hello", name, "from", url

然后这样调用

> python manage.py hello -n Joe -u reddit.com
hello Joe from reddit.com

> python manage.py hello --name=Joe --url=reddit.com
hello Joe from reddit.com

向管理器实现添加选项

选项也可以传递给Manager实例。这允许您设置传递给应用程序的选项,而不是单个命令。例如,您可能希望有一个标志来设置应用程序的配置文件。假设您使用工厂函数创建应用程序:

def create_app(config=None):

    app = Flask(__name__)
    if config is not None:
        app.config.from_pyfile(config)
    # configure your app...
    return app

您希望能够在命令行上定义配置参数——例如,如果您有一个命令来设置数据库,那么您肯定希望为生产和开发使用不同的配置文件。

为了传递配置参数,使用管理器实例的add_option()方法。它采用与Option相同的参数:

manager.add_option('-c', '--config', dest='config', required=False)

与任何其他Flask-Script配置一样,您可以在脚本模块的任何位置调用它,但是必须在调用manager.run()之前调用它。

假设您有以下命令:

@manager.command
def hello(name):
    uppercase = app.config.get('USE_UPPERCASE', False)
    if uppercase:
        name = name.upper()
    print "hello", name

可以这样运行

> python manage.py -c dev.cfg hello joe
hello JOE

假设在您的dev.cfg文件中USE_UPPERCASE设置为True。

还要注意,“config”选项没有传递给命令。事实上,这个用法:

> python manage.py hello joe -c dev.cfg

将显示错误消息,因为-c选项不属于hello命令。

您可以将相同名称的选项附加到不同的级别;这允许你添加一个选项到你的应用程序设置代码,而不需要检查它是否与命令冲突:

@manager.option('-n', '--name', dest='name', default='joe')
@manager.option('-c', '--clue', dest='clue', default='clue')
def hello(name, clue):
    uppercase = app.config.get('USE_UPPERCASE', False)
    if uppercase:
        name = name.upper()
        clue = clue.upper()
    print "hello {0}, get a {1}!".format(name, clue)
> python manage.py -c dev.cfg hello -c cookie -n frank
hello FRANK, get a COOKIE!

注意,目标变量(对应于dest值的命令参数)必须仍然不同;这是Python参数解析器的一个限制。

为了让管理器选项起作用,必须将工厂函数(而不是烧瓶实例)传递给管理器构造函数。本文提供了一个简单但完整的示例。

点此查看示例

让用户输入

Flask-Script附带一组帮助函数,用于从命令行获取用户输入。例如:

from flask_script import Manager, prompt_bool

from myapp import app
from myapp.models import db

manager = Manager(app)

@manager.command
def dropdb():
    if prompt_bool(
        "Are you sure you want to lose all your data"):
        db.drop_all()

然后运行

> python manage.py dropdb
Are you sure you want to lose all your data ? [N]

有关各种提示函数的详细信息,请参阅API

默认的命令

Flask-Script有两个现成的命令可以添加和定制:Server和Shell。

  • runserver
    • Server命令运行Flask开发服务器。
from flask_script import Server, Manager
from myapp import create_app

manager = Manager(create_app)
manager.add_command("runserver", Server())

if __name__ == "__main__":
    manager.run()

然后运行

python manage.py runserver

Server命令有许多命令行参数-运行python management .py runserver -?了解详情。你可以在构造函数中重新定义默认值:

server = Server(host="0.0.0.0", port=9000)

不用说,开发服务器并不打算用于生产。

runserver最常见的用例是运行调试服务器来调查问题。因此,如果没有在配置文件中设置此选项,则默认选项是启用调试和自动重新加载。

不幸的是,弗拉斯克目前(截至2014年5月)默认将调试配置参数设置为False。在此更改之前,您可以安全地将DEBUG=None添加到烧瓶配置中。然后,Flask-Script的runserver将打开调试,但其他所有操作都将把它视为关闭。

为了防止误解(毕竟,调试模式是一个严重的安全漏洞),当Flask-Script将None默认值设置为True时,会打印一个警告。您可以显式地打开调试来消除此警告。

shell

Shell命令启动一个Python Shell。你可以传入一个make_context参数,它必须是一个返回dict的可调用参数。默认情况下,这只是一个返回烧瓶应用实例的dict:

from flask_script import Shell, Manager

from myapp import app
from myapp import models
from myapp.models import db

def _make_context():
    return dict(app=app, db=db, models=models)

manager = Manager(create_app)
manager.add_command("shell", Shell(make_context=_make_context))

如果您想在shell中包含一组默认值,以节省输入大量导入语句的时间,那么这是非常方便的。

如果安装了Shell命令,那么它将使用IPython,否则它将默认使用标准Python Shell。您可以通过两种方式禁用这种行为:将use_ipython参数传递给Shell构造函数,或者在命令行中传递标志–no-ipython:

shell = Shell(use_ipython=False)

还有一个shell装饰器,你可以用一个上下文函数:

@manager.shell
def make_shell_context():
    return dict(app=app, db=db, models=models)

这将启用shell命令,并启用默认值:

> python manage.py shell

默认情况下包含默认命令shell和runserver,并为这些命令提供默认选项。如果希望用不同的命令替换它们,只需用add_command()或装饰器覆盖即可。如果将with_default_commands=False传递给Manager构造函数,这些命令将不会被加载:

manager = Manager(app, with_default_commands=False)

Sub-Managers

子管理器是作为命令添加到另一个管理器的管理器实例

创建子管理器:

def sub_opts(app, **kwargs):
    pass
sub_manager = Manager(sub_opts)

manager = Manager(self.app)
manager.add_command("sub_manager", sub_manager)

如果将选项附加到sub_manager, sub_opts过程将接收它们的值。为了方便,您的应用程序在app中被传递。

如果sub_opts返回一个非None的值,这个值将替换传递的app值。这样,你就可以实现一个子管理器来替代整个应用程序。一个用例是创建一个单独的管理应用程序来提高安全性:

def gen_admin(app, **kwargs):
    from myweb.admin import MyAdminApp
    ## easiest but possibly incomplete way to copy your settings
    return MyAdminApp(config=app.config, **kwargs)
sub_manager = Manager(gen_admin)

manager = Manager(MyApp)
manager.add_command("admin", sub_manager)
> python manage.py runserver
[ starts your normal server ]
> python manage.py admin runserver
[ starts an administrative server ]

您可以级联子管理器,即将一个子管理器添加到另一个子管理器。

子管理器不会获得添加到自身的默认命令(默认情况下)

扩展开发人员注释

扩展开发人员可以轻松地在扩展中创建方便的子管理器实例,使用户能够轻松地使用扩展的所有可用命令。

下面是一个数据库扩展如何提供的示例(例如database.py):

manager = Manager(usage="Perform database operations")

@manager.command
def drop():
    "Drops database tables"
    if prompt_bool("Are you sure you want to lose all your data"):
        db.drop_all()


@manager.command
def create(default_data=True, sample_data=False):
    "Creates database tables from sqlalchemy models"
    db.create_all()
    populate(default_data, sample_data)


@manager.command
def recreate(default_data=True, sample_data=False):
    "Recreates database tables (same as issuing 'drop' and then 'create')"
    drop()
    create(default_data, sample_data)


@manager.command
def populate(default_data=False, sample_data=False):
    "Populate database with default data"
    from fixtures import dbfixture

    if default_data:
        from fixtures.default_data import all
        default_data = dbfixture.data(*all)
        default_data.setup()

    if sample_data:
        from fixtures.sample_data import all
        sample_data = dbfixture.data(*all)
        sample_data.setup()

然后用户可以将子管理器注册到他们的主管理器(在management .py中):

manager = Manager(app)

from flask_database import manager as database_manager
manager.add_command("database", database_manager)

这些命令将会提供:

> python manage.py database

 Please provide a command:

 Perform database operations
  create    Creates database tables from sqlalchemy models
  drop      Drops database tables
  populate  Populate database with default data
  recreate  Recreates database tables (same as issuing 'drop' and then 'create')

错误处理

用户不喜欢看到堆栈跟踪,但是开发人员希望它们用于bug报告。

因此,flask_script.commands提供了一个InvalidCommand错误类,它在报告时不应该打印堆栈跟踪。

在您的命令处理程序:

from flask_script.commands import InvalidCommand

[… if some command verification fails …]
class MyCommand(Command):
    def run(self, foo=None, bar=None):
        if foo and bar:
            raise InvalidCommand("Options foo and bar are incompatible")

在你的主循环中:

try:
    MyManager().run()
except InvalidCommand as err:
    print(err, file=sys.stderr)
    sys.exit(1)

这样,如果一些插件代码提供了您想要使用的Flask-Script挂钩,您就可以维护互操作性,反之亦然。

访问本地代理

管理器在Flask测试上下文中运行命令。这意味着您可以在适当的地方访问请求本地代理,比如current_app,它可以被扩展使用。

发表回复