6 сентября 2012 г.

Flask-Script или улучшенный manage.py для Flask приложений

Наверное, ни один из моих Django-проектов не обходился без пары-тройки кастомных команд, благо эта возможность была одним из столбов архитектуры Django-проекта. Да, написание кода для этих кастомных команд иногда заставляло схватываться за голову и вопрошать: "Why so hard?", но сегодня я не про это.

А про расширение Flask-Script, которое добавляет схожий функционал уже в Flask-приложения и проекты. Думаю, про это расширения многие наслышаны, но до недавнего времени использовать его было очень затруднительно из-за того, что автор на какое-то время ушел от дел и форки приложения добавляющие и правящие одну или две неточности появлялись как грибы после дождя. Но, хвала высшим силам, в прошлом месяце Шон Линч решил покончить с беспределом и выпустил версию 0.4.0, которая просто не оставляет нам оправданий на вопрос, почему до сих пор Flask-Script не добавлен в список зависимостей нашего проекта.

Использовать расширение очень просто и понятно, инициализируем инстанс менеджера, добавляем кастомные команды, запускаем менеджер, если был вызван Python-скрипт. Пример использования, app.py:

from flask import Flask
from flask.ext.script import Manager


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

@manager.command
def hello():
    """
    Hello, world!
    """
    print('Hello, world!')


if __name__ == '__main__':
    manager.run()
(env)$ python app.py
Please provide a command
  hello      Hello, world!
  runserver  Runs the Flask development server i.e. app.run()
  shell      Runs a Python shell inside Flask application context.

И так как в дефолтной поставке сразу доступны две команды, runserver и shell, мы можем запускать девсервер даже не добавляя строчку app.run() в наш app.py, удобно.

Но описанный подход подходит только для демонстрационных или маленьких приложений, в реальной же жизни мы переносим инициализацию и/или запуск менеджера в давно знакомый нам manage.py:

from flask.ext.script import Manager

from app import app


manager = Manager(app)


@manager.command
def hello():
    """
    Hello, world!
    """
    print('Hello, world!')


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

Лирическое отступление. Если честно, я еще точно не определился с тем как более православно объявлять инстанс менеджера и где размещать все кастомные команды. Внутри, хочется объявлять менеджер в app.py, а затем все команды описывать в commands.py, но пока весь код для кастомных команд хранится именно в manage.py, а вот инстанс менеджера таки инициализируется в app.py. Однако почему-то не могу я такой подход порекомендовать всем, так что остановлюсь пока на варианте автора расширения.

Для любителей классов предусмотрена возможность наследовать команды от Command базового класса, но мне по душе ближе использовать функции, этого ой как не хватало в стандартной поставке Django.

В остальном у проекта есть отличная документация, так что пользуйтесь, пользуйтесь и еще раз пользуйтесь!

blog comments powered by Disqus