Расширение возможностей Flask с помощью Flask-Script



Расширения Flask

Расширения Flask — это пакеты, которые можно установить, чтобы расширить возможности Flask. Их суть в том, чтобы обеспечить удобный и понятный способ интеграции пакетов во Flask. Посмотреть все доступные расширения можно здесь. На странице есть пакеты, возможности которых варьируются от отправки email до создания полноценных интерфейсов администратора. Важно помнить, что расширять возможности Flask можно не только с помощью его расширений. На самом деле, подойдет любой пакет из стандартной библиотеки Python или PyPi. Оставшаяся часть урока посвящена тому, как установить и интегрировать удобное расширение для Flask под названием Flask-Script.

Расширение Flask-Script

Flask-Script — это удобное миниатюрное расширение, которое позволяет создавать интерфейсы командной строки, запускать сервер и консоль Python в контексте приложений, делать определенные переменные видимыми в консоли автоматически и так далее.

Стоит напомнить то, что обсуждалось в уроке «Основы Flask». Для запуска сервера разработки на конкретном хосте и порте, их нужно передать в качестве аргументов-ключевых слов методу run():

if __name__ == "__main__":
    app.run(debug=True, host="127.0.0.10", port=9000)

Проблема в том, что такой подход не гибкий. Намного удобнее передать хост и порт в виде параметров командной строки при запуске сервера. Flask-Script позволяет сделать это. Установить Flask-Script можно с помощью pip:

(env) [email protected]:~/flask_app$ pip install flask-script

Чтобы использовать Flask-Script сперва нужно импортировать класс Manager из пакета flask_script и создать экземпляр объекта Manager, передав ему экземпляр приложения. Таким образом расширения Flask интегрируются. Сначала импортируется нужный класс из пакета, а затем создается экземпляр с помощью передачи ему экземпляра приложения. Нужно открыть файл main2.py и изменить его следующим образом:

from flask import Flask, render_template
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

#...

Созданный объект Manager также имеет метод run(), который помимо запуска сервера разработки может считывать аргументы командной строки. Следует заменить строку app.run(debug=True) на manager.run(). К этому моменту main2.py должен выглядеть вот так:

from flask import Flask, render_template
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@app.route('/')
def index():
    return render_template('index.html', name='Jerry')

@app.route('/user/<int:user_id>/')
def user_profile(user_id):
    return "Profile page of user #{}".format(user_id)

@app.route('/books/<genre>/')
def books(genre):
    return "All Books in {} category".format(genre)

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

Теперь у приложения есть доступ к базовым командам. Чтобы посмотреть, какие из них доступны, необходимо запустить файл main2.py:

(env) [email protected]:~/flask_app$ python main2.py
usage: main2.py [-?] {shell,runserver} ...

positional arguments:
  {shell,runserver}
    shell		Runs  a  Python shell inside Flask application context.
    runserver		Runs the Flask development server  i.e.  app.run()

optional arguments:
-?, --help		show this  help message and  exit

Как показывает вывод, сейчас есть всего две команды: shell и runserver. Начнем с команды runserver.

runserver запускает веб-сервер. По умолчанию, он запускается на 127.0.0.1 на порте 5000. Чтобы увидеть варианты для любой команды нужно ввести --help и саму команду. Например:

(env) [email protected]:~/flask_app$ python main2.py runserver --help
usage: main2.py runserver [-?] [-h  HOST] [-p  PORT]  [--threaded]
			  [--processes PROCESSES] [--passthrough-errors] [-d]
			  [-D] [-r] [-R] [--ssl-crt SSL_CRT]
			  [--ssl-key SSL_KEY]

Runs the Flask development server  i.e.  app.run()

optional arguments:
  -?, --help 		show this  help message and  exit
  -h HOST, --host HOST
  -p PORT, --port PORT
  --threaded
  --processes PROCESSES
  --passthrough-errors
  -d, --debug 		enable the Werkzeug debugger (DO NOT use in production
			code)
  -D, --no-debug	disable the Werkzeug debugger
  -r, --reload		monitor Python files for changes (not 100% safe for
			production use)
  -R, --no-reload 	do not  monitor Python files for changes
  --ssl-crt SSL_CRT 	Path to ssl certificate
  --ssl-key SSL_KEY 	Path to ssl key

Самые широко используемые варианты для runserver — это --host и --post. С их помощью можно запустить сервер разработки на конкретном интерфейсе и порте. Например:

(env) [email protected]:~/flask_app$ python main2.py runserver  --host=127.0.0.2 --port 8000
* Running on http://127.0.0.2:8000/ (Press CTRL+C to quit)

По умолчанию команда runserver запускает сервер без отладчика. Включить его вручную можно следующим образом:

(env) [email protected]:~/flask_app$ python main2.py runserver -d -r
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 250-045-653
 * Running on http://127.0.0.1:5000/ (Press CTRL+C  to  quit)

Более простой способ запустить отладчик — выбрать значение True для атрибута debug у экземпляра объекта (app). Для этого нужно открыть main2.py и изменить файл следующим образом:

#...
app = Flask(__name__)
app.debug = True
manager = Manager(app)
#...

Далее о команде shell.

shell запускает консоль Python в контексте приложения Flask. Это значит, что все объекты внутри контекстов приложения и запроса будут доступны в консоли без создания дополнительных контекстов. Для запуска консоли нужно ввести следующую команду.

(env) [email protected]:~/flask_app$ python main2.py shell

Получим доступ к определенным объектам.

>>>
>>> from flask import current_app, url_for, request
>>>
>>> current_app.name
'main2'
>>>
>>>
>>> url_for("user_profile", user_id=10)
'/user/10/'
>>>
>>> request.path
'/'
>>>

Как и ожидалось, это можно сделать, не создавая контексты приложения и запроса.

Создание команд

Когда экземпляр Manager создан, можно приступать к созданию собственных команд. Есть два способа:

  1. С помощью класса Command
  2. С помощью декоратора @command
Создание команд с помощью класса Command

В файле main2.py добавим класс Faker:

#...
from flask_script import Manager, Command
#...

manager = Manager(app)

class Faker(Command):
    'Команда для добавления поддельных данных в таблицы'
    def run(self):
        # логика функции
        print("Fake data entered")

@app.route('/')
#...

Команда Faker была создана с помощью наследования класса Command. Метод run() вызывается при исполнении команды. Чтобы выполнить команду через командную строку, ее нужно добавить в экземпляр Manager с помощью метода add_command():

#...
class Faker(Command):
    'Команда для добавления поддельных данных в таблицы'
    def run(self):
        # логика функции
        print("Fake data entered")

manager.add_command("faker", Faker())
#...

Теперь нужно снова вернуться в терминал и запустить файл main2.py:

(env) [email protected]:~/flask_app$ python main2.py
usage: main2.py [-?] {faker,shell,runserver} ...

positional arguments:
  {faker,shell,runserver}
    faker  		Команда для добавления поддельных данных в таблицы
    shell		Runs  a  Python shell inside Flask application context.
    runserver		Runs the Flask development server  i.e.  app.run()

optional arguments:
-?, --help		show this  help message and  exit

Стоит обратить внимание, что теперь, в дополнение к shell и runserver, есть команда faker. Описание перед самой командой взято из строки документации класса Faker. Для запуска нужно ввести следующую команду:

(env) [email protected]:~/flask_app$ python main2.py faker
Fake data entered
Создание команд с помощью декоратора @command

Создание команд с помощью класса Command достаточно объемно. Как вариант, можно использовать декоратор @command экземпляра класса Manager. Для этого нужно открыть файл main2.py и изменить его следующим образом:

#...
manager.add_command("faker", Faker())

@manager.command
def foo():
    "Это созданная команда"
    print("foo command executed")

@app.route('/')
#...

Была создана простая команда foo, которая выводит foo command executed при вызове. Декоратор @command автоматически добавляет команду к существующему экземпляру Manager, так что не нужно вызывать метод add_command(). Чтобы увидеть, как используются команды, нужно вернуться обратно в терминал и запустить main2.py.

(env) [email protected]:~/flask_app$ python main2.py
usage: main2.py [-?] {faker,foo,shell,runserver} ...

positional arguments:
  {faker,foo,shell,runserver}
    faker  		Команда для добавления поддельных данных в таблицы
    foo 		Это созданная команда
    shell		Runs  a  Python shell inside Flask application context.
    runserver		Runs the Flask development server  i.e.  app.run()

optional arguments:
-?, --help		show this  help message and  exit

Поскольку команда foo теперь доступна, ее можно исполнить, введя следующую команду.

(env) [email protected]:~/flask_app$ python main2.py foo
foo command executed

Автоматический импорт объектов

Импорт большого количества объектов в командной строке может быть утомительным. С помощью Flask-Script объекты можно сделать видимыми в терминале без явного импорта.

Команда Shell запускает оболочку. Функция конструктора оболочки Shell принимает аргумент-ключевое слово make_context. Аргумент, передаваемый make_context должен быть вызываемым и возвращать словарь. По умолчанию вызываемый объект возвращает словарь, содержащий только экземпляр приложения, то есть app. Это значит, что по умолчанию в оболочке можно получить доступ только к экземпляру приложения (app), специально не импортируя его. Чтобы изменить это поведение, нужно назначить новому объекту (функции), поддерживающему вызов, make_context. Это вернет словарь с объектами, к которым требуется получить доступ внутри оболочки.

Откроем файл main2.py, чтобы добавить следующий код после функции foo().

#...
from flask_script import Manager, Command, Shell
#...

def shell_context():
    import os, sys
    return dict(app=app, os=os, sys=sys)

manager.add_command("shell", Shell(make_context=shell_context))
#...

Здесь вызываемой функции shell_context() передается аргумент-ключевое слово make_context. Функция shell_context возвращает словарь с тремя объектами: app, os и sys. В результате, внутри оболочки теперь есть доступ к этим объектам, хотя их импорт не производился.

(env) [email protected]:~/flask_app$ python main2.py shell
>>>
>>> app
<Flask 'main2'>
>>>
>>> os.name
'posix'
>>>
>>> sys.platform
'linux'
>>>
>>>