• en
  • Language: ru
  • Documentation version: 1.1.x

Проектные решения во Flask

Если вам интересно, почему Flask делает определенные вещи так, а не иначе, этот раздел для вас. Это должно дать вам представление о некоторых проектных решениях, которые поначалу могут показаться произвольными и удивительными, особенно при прямом сравнении с другими фреймворками.

Явный объект приложения

Веб-приложение Python, основанное на WSGI, должно иметь один центральный вызываемый объект, реализующий собственно приложение. Во Flask это экземпляр класса Flask. Каждое приложение Flask должно само создавать экземпляр этого класса и передавать ему имя модуля, но почему Flask не может сделать это сам?

Без такого явного объекта применения следующий код:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World!'

Вместо этого будет выглядеть так:

from hypothetical_flask import route

@route('/')
def index():
    return 'Hello World!'

Для этого есть три основные причины. Самая важная из них заключается в том, что неявные объекты приложений требуют, чтобы в данный момент времени существовал только один экземпляр. Существуют способы сымитировать несколько приложений с помощью одного объекта приложения, например, поддерживать стек приложений, но это вызывает некоторые проблемы, которые я не буду здесь подробно описывать. Теперь вопрос в том, когда микрофреймворку нужно более одного приложения одновременно? Хороший пример - модульное тестирование. Когда вы хотите что-то протестировать, может быть очень полезно создать минимальное приложение для проверки определенного поведения. Когда объект приложения будет удален, все, что он выделил, будет освобождено снова.

Еще одна вещь, которая становится возможной, когда у вас есть явный объект, лежащий в вашем коде, заключается в том, что вы можете подклассифицировать базовый класс (Flask), чтобы изменить определенное поведение. Это было бы невозможно без хаков, если бы объект был создан заранее для вас на основе класса, который вам не доступен.

Но есть еще одна очень важная причина, по которой Flask зависит от явного инстанцирования этого класса: имя пакета. Всякий раз, когда вы создаете экземпляр Flask, вы обычно передаете ему __name__ в качестве имени пакета. Flask зависит от этой информации для правильной загрузки ресурсов относительно вашего модуля. Благодаря выдающейся поддержке отражения в Python он может получить доступ к пакету, чтобы выяснить, где хранятся шаблоны и статические файлы (см. open_resource()). Теперь, очевидно, существуют фреймворки, которым не нужна никакая конфигурация, и они все равно смогут загружать шаблоны относительно вашего модуля приложения. Но для этого им приходится использовать текущий рабочий каталог, что является очень ненадежным способом определения местонахождения приложения. Текущий рабочий каталог зависит от процесса, и если вы запускаете несколько приложений в одном процессе (что может произойти в веб-сервере без вашего ведома), пути будут отличаться. Хуже того: многие веб-серверы устанавливают рабочий каталог не в каталог вашего приложения, а в корень документа, который не обязательно должен быть той же самой папкой.

Третья причина - «явное лучше неявного». Этот объект - ваше приложение WSGI, вам не нужно больше ничего помнить. Если вы хотите применить промежуточное ПО WSGI, просто оберните его и готово (хотя есть лучшие способы сделать это так, чтобы не потерять ссылку на объект приложения wsgi_app()).

Кроме того, такая конструкция позволяет использовать фабричную функцию для создания приложения, что очень полезно для модульного тестирования и подобных вещей (Заводы по производству приложений).

Система маршрутизации

Flask использует систему маршрутизации Werkzeug, которая была разработана для автоматического упорядочивания маршрутов по сложности. Это означает, что вы можете объявлять маршруты в произвольном порядке, и они все равно будут работать так, как ожидается. Это требование необходимо, если вы хотите правильно реализовать маршрутизацию на основе декораторов, поскольку декораторы могут быть запущены в неопределенном порядке, когда приложение разделено на несколько модулей.

Еще одним дизайнерским решением в системе маршрутизации Werkzeug является то, что маршруты в Werkzeug стараются обеспечить уникальность URL. Werkzeug заходит довольно далеко в этом вопросе, поскольку автоматически перенаправляет на канонический URL, если маршрут неоднозначен.

Единый механизм шаблонов

Flask выбирает один шаблонизатор: Jinja2. Почему у Flask нет подключаемого интерфейса шаблонизатора? Очевидно, что вы можете использовать другой шаблонизатор, но Flask все равно настроит Jinja2 для вас. Хотя ограничение, что Jinja2 всегда настроен, вероятно, исчезнет, решение о подключении одного шаблонизатора и использовании его не исчезнет.

Шаблонные движки подобны языкам программирования, и каждый из этих движков имеет определенное представление о том, как все работает. На поверхности все они работают одинаково: вы говорите движку оценить шаблон с набором переменных и принимаете возвращаемое значение в виде строки.

Но на этом сходства заканчиваются. Например, Jinja2 имеет обширную систему фильтров, определенный способ наследования шаблонов, поддержку многократно используемых блоков (макросов), которые можно использовать изнутри шаблонов, а также из кода Python, использует Unicode для всех операций, поддерживает итеративный рендеринг шаблонов, настраиваемый синтаксис и многое другое. С другой стороны, такой движок, как Genshi, основан на оценке потока XML, наследовании шаблонов с учетом доступности XPath и многом другом. С другой стороны, Mako обращается с шаблонами аналогично модулям Python.

Когда речь идет о соединении шаблонизатора с приложением или фреймворком, это нечто большее, чем просто рендеринг шаблонов. Например, Flask использует обширную поддержку автозамены в Jinja2. Также он предоставляет способы доступа к макросам из шаблонов Jinja2.

Слой абстракции шаблонов, который не отнимал бы уникальные особенности шаблонизаторов, - это целая наука и слишком большая задача для такого микрофреймворка, как Flask.

Кроме того, расширения могут легко зависеть от наличия одного языка шаблонов. Вы можете легко использовать свой собственный язык шаблонов, но расширение все равно может зависеть от самого Jinja.

Микро с зависимостями

Почему Flask называет себя микрофреймворком, но при этом зависит от двух библиотек (а именно Werkzeug и Jinja2). А почему бы и нет? Если мы посмотрим на Ruby сторону веб-разработки, то там есть протокол, очень похожий на WSGI. Только там он называется Rack, но кроме этого он очень похож на WSGI для Ruby. Но почти все приложения на земле Ruby работают с Rack не напрямую, а поверх одноименной библиотеки. Эта библиотека Rack имеет два эквивалента в Python: WebOb (ранее Paste) и Werkzeug. Paste все еще существует, но, насколько я понимаю, она вроде как устарела в пользу WebOb. Разработка WebOb и Werkzeug началась бок о бок с похожими идеями: быть хорошей реализацией WSGI, чтобы другие приложения могли воспользоваться этим.

Flask - это фреймворк, который использует преимущества работы, уже проделанной Werkzeug для правильного взаимодействия с WSGI (что иногда может быть сложной задачей). Благодаря последним изменениям в инфраструктуре пакетов Python, пакеты с зависимостями больше не являются проблемой, и существует очень мало причин против наличия библиотек, которые зависят от других.

Месторасположение нитей

Flask использует локальные объекты потока (локальные объекты контекста, на самом деле они поддерживают контексты greenlet) для запроса, сессии и дополнительного объекта, на который вы можете поместить свои собственные вещи (g). Почему так происходит и не является ли это плохой идеей?

Да, обычно использование локальных потоков - не самая лучшая идея. Они создают проблемы для серверов, не основанных на концепции потоков, и делают большие приложения более сложными в обслуживании. Однако Flask просто не предназначен для больших приложений или асинхронных серверов. Flask стремится сделать написание традиционного веб-приложения быстрым и легким.

Также смотрите раздел Стать большим документации для вдохновения при создании более крупных приложений на основе Flask.

Что такое Flask и что Flask не является таковым

Flask никогда не будет иметь слоя базы данных. У него не будет библиотеки форм или чего-либо еще в этом направлении. Сам Flask просто соединяется с Werkzeug для реализации правильного WSGI-приложения и с Jinja2 для работы с шаблонами. Он также связывается с несколькими общими стандартными библиотечными пакетами, такими как логирование. Все остальное - на усмотрение расширений.

Почему так происходит? Потому что у людей разные предпочтения и требования, и Flask не сможет удовлетворить их, если будет внедрять все это в ядро. Большинству веб-приложений в той или иной степени необходим механизм шаблонов. Однако не каждому приложению нужна база данных SQL.

Идея Flask заключается в том, чтобы создать хорошую основу для всех приложений. Все остальное зависит от вас или расширений.