[Из песочницы] Flask. Наполняем «флягу» функционалом
Предисловие В прошлом году решил для себя вплотную познакомиться c Python, а в последствии перебраться на него с PHP. На данный момент моя стезя — веб-разработка, а потому осваивать новый язык я начал именно со стороны веба, в частности, с обзора доступных фреймворков и проектов на них. Познакомившись с возможностями TurboGears, web2py, Django, я всё таки поддался «тренду» и погрузился в мир Django.На протяжении почти года я честно пытался подружиться с ним. Написал несколько простеньких проектов, но монструозность фреймворка отпугивала, обилие «батареек» путало выбор, а с некоторыми ограничениями не хотелось мириться. Душа требовала лаконичности и однозначности, что в конечном счете привело меня к знакомству с Flask. Изучив документацию по фреймворку и смежным проектам (Jinja2, Werkzeug), я проникся идеологией и стал вплотную изучать фреймворк.
Flask позиционируется как расширяемый микрофреймворк. Это означает наличие лишь необходимого минимума функционала, но в то же время возможность добавить оный посредством расширений до требуемого проекту уровня.
Сей эпос — это мой опыт под использованию Flask и расширений для него, а точнее, попытка собрать воедино и на русском то, что может пригодиться при создании проектов практически любого уровня.
Структура и конфигурация Для каждого проекта я придерживаюсь типовой структуры, описанной ниже. Всё достаточно тривиально и знакомо программистам Django: app/ --commands/ --migrations/ --static/ --templtaes/ --app.py --config.py --forms.py --manage.py --models.py --views.py Каталог commands содержит команды для обслуживания приложения, подключаемые в модуле manage.py. Каталог migrations — файлы и конфигурацию миграций. Обычно создается автоматически при инициализации миграций. Каталог static — ресурсы проекта: js, css, scss и картинки. Каталог templates — шаблоны. Файл app.py — это головной модуль приложения, где определяются основные настройки и регистрируются расширения, он же реализует и веб-сервер. Файл manage.py служит для управления и обслуживания проектом. Файл config.py содержит объект конфигурации приложения. Отмечу, что Flask можно конфигурировать различными способами, но мне наиболее удобным показался способ на основе объектов. В упрощенном виде содержимое файла выглядит так: config.py import os basedir = os.path.abspath (os.path.dirname (__file__))
class Config (object): DEBUG = False CSRF_ENABLED = True WTF_CSRF_SECRET_KEY = 'dsofpkoasodksap' SECRET_KEY = 'zxczxasdsad' SQLALCHEMY_DATABASE_URI = 'mysql+mysqlconnector://webuser: web_password@localhost/webuser_db'
class ProductionConfig (Config): DEBUG = False
class DevelopConfig (Config): DEBUG = True ASSETS_DEBUG = True А его применение так: app.py app.config.from_object ('config.DevelopConfig') Для крупных проектов официальная документация рекомендует дробить функционал на так называемые blueprints — модули, структурно похожие на приложение Flask, а сам проект организовывать в пакет python. Но сегодня не об этом.Расширения Flask-SQLAlchemy Любое серьезное приложение использует базы данных. Данное расширение дружит Flask с самой популярной на Python ORM-библиотекой — SQLAlchemy, позволяя использовать любые поддерживаемые ей СУБД, а также отображение таблиц в объекты Python, аналогично Django. Впрочем, SQLAlchemy позволяет обойтись и без ORM.Использование # config.py class Config (object): … # определяем DSN в конфигурации SQLALCHEMY_DATABASE_URI = 'mysql+mysqlconnector://webuser: web_password@localhost/webuser_db'
# app.py # импортируем расширение from flask.ext.sqlalchemy import SQLAlchemy
# инициализируем объект БД db = SQLAlchemy (app)
# models.py from app import db
# Модель User — отображение таблицы users в БД class User (db.Model): __tablename__ = 'users'
id = db.Column (db.Integer, primary_key=True)
email = db.Column (db.String (120), unique=True, nullable=False)
password = db.Column (db.String (255))
Документация: pythonhosted.org/Flask-SQLAlchemy/Flask-Script
Добавляет поддержку обслуживающих проект скриптов: запуск dev-сервера, миграции баз данных, cron-задачи и тому подобное. Следуя рекомендациям, я создаю для каждого проекта файл manage.py, где добавляются все необходимые для обслуживания команды. По-умолчанию доступна команда runserver. Запуск команд осуществляется следующим образом:
$ python manage.py command
from models import * migrate = Migrate (app, db)
# Инициализируем менеджер manager = Manager (app) # Регистрируем команду, реализованную в виде потомка класса Command manager.add_command ('db', MigrateCommand)
if __name__ == '__main__': manager.run () Документация: flask-script.readthedocs.org/en/latest/Flask-Migrate Позволяет настроить миграции для ORM SQLAlchemy. Пакет предоставляет класс MigrateCommand, который можно использовать в связке с вышеописанным расширением Flask-Script. Для использования миграций необходимо подключить команду (пример выше), произвести начальную инициализацию, выполнив manage.py db init, затем использовать действия migrate, upgrade и downgrade данной команды для управления миграциями. Стоит отметить, что список действий для команды и их краткое описание можно получить, выполнив manage.py db help.Документация: flask-migrate.readthedocs.org/en/latest/
Flask-WTF Реализует привязку к WTForms — замечательной библиотеке для работы с формами. Опять же, налицо аналогия с Django. В коробке: солидный набор классов полей и валидаторов для них, наследование, вложенные формы и многое другое.forms.py from flask_wtf import Form from wtforms import StringField, PasswordField, TextAreaField, SelectField from wtforms.validators import Email, DataRequired, EqualTo
class LoginForm (Form): email = StringField ('E-mail', validators=[Email (), DataRequired ()]) password = PasswordField ('Пароль', validators=[DataRequired ()])
class RegistrationForm (LoginForm): password_repeat = PasswordField ('Повторите пароль', validators=[DataRequired (), EqualTo ('password')]) Также есть расширение wtforms-alchemy для создания форм на основе моделей SQLAlchemy. Наткнулся на него совсем недавно, посему опыта работы пока нет. Впрочем, думаю, и здесь применима аналогия с Django.Документация: flask-wtf.readthedocs.org/en/latest/
Flask-Login Добавляет базовую поддержку авторизации и пользовательских сессий. Подключение данного расширения требует достаточно много действий, поэтому попытаюсь быть максимально лаконичным и пропущу реализацию входа и выхода пользователей.Использование Flask-Login # app.py # подключаем плагин from flask.ext.login import LoginManager, current_user
# Инициализируем его и задаем действие «входа» login_manager = LoginManager () login_manager.init_app (app) login_manager.login_view = 'login'
# Задаем обработчик, возвращающий пользователя по Id, либо None. Здесь пользователь запрашивается из базы. @login_manager.user_loader def load_user (userid): from models import User return User.query.get (int (userid))
# Задаем обработчик before_request, в котором добавляем к глобально-локальному контексту текущего пользователя @app.before_request def before_request (): g.user = current_user
# models.py class User (db.Model): __tablename__ = 'users'
id = db.Column (db.Integer, primary_key=True) email = db.Column (db.String (120), unique=True, nullable=False) password = db.Column (db.String (255))
# Расширением предъявляются некоторые требования к классу User, а именно реализация следующих методов def is_authenticated (): return True
def is_active (): return True
def is_anonymous (): return False
def get_id (self): return str (self.id) Документация: flask-login.readthedocs.org/en/latest/Flask-Bcrypt Добавляет функционал для хеширования и проверки паролей.models.py from flask.ext.bcrypt import generate_password_hash, check_password_hash
class User (db.Model): … def check_password (self, password): return check_password_hash (self.password, password)
@staticmethod def hash_password (password): return generate_password_hash (password) Flask-Assets Дружит Flask с библиотекой webassets, позволяя невероятно изящно работать с ресурсами проекта. Умеет объединять ресурсы в пакеты, компилировать scss (sass) и less, минифицировать js и css и удобно подключать их в шаблонах.Использование # app.py # Подключаем from flask.ext.assets import Environment, Bundle
assets = Environment (app)
# Формируем и регистрируем пакеты js = Bundle ('jquery.js', 'jquery.file-upload.js', filters='jsmin', output='assets/jquery-min.js') css = Bundle ('main.css', 'form.css', 'flashes.css', filters='cssmin', output='assets/all-min.css') assets.register ('js_all', js) assets.register ('css_all', css)
# templates/index.html {% assets «js_all» %} {% endassets %} {% assets «css_all» %} {% endassets %} Указав в пакете (Bundle) параметр filters, мы заставим пропустить файлы пакета через заданный фильтр (ы). В нашем случае файлы минифицируются и объяденяться в один. Некоторые фильтры потребуют установки дополнительных python-модулей.Если в конфигурации задан параметр ASSETS_DEBUG = True, то файлы пакетов не будут пропускаться через фильтры и склеиваться, а в шаблоне для каждого файла будет сгенерирован отдельный url.
P.S. При достаточно большом количестве ресурсов, следует вынести функционал по формированию пакетов (Bundle) и их регистрации в отдельный файл — например, assets.py.
Документация: flask-assets.readthedocs.org/en/latest/
Flask-DebugToolbar Какая разработка обойдется без удобного дебаггера? Расширение добавляет debug-панель, портированную из Django, с исчерпывающей информацией о запросе. Панель отображается при заданном в конфигурации параметре DEBUG = True.app.py # config.py class Config (object): … # Задаем токен для генерации cookie SECRET_KEY = 'xv3gavkxc04n3mzx7oksd6q'
# app.py # Подключаем from flask_debugtoolbar import DebugToolbarExtension
# Регистрируем dtb = DebugToolbarExtension (app) Документация: flask-debugtoolbar.readthedocs.org/en/latest/Вместо заключения В статье представлены расширения, которые мне довелось использовать в своих проектах, однако это далеко не полный список того, что уже существует для Flask. Более исчерпывающий список актуальных расширений представлен на официальном сайте фреймворка по ссылке flask.pocoo.org/extensions.