Как стать автором
Обновить

Мега-Учебник Flask, Часть XV: Улучшение структуры приложения

Время на прочтение18 мин
Количество просмотров50K

(издание 2018)


Miguel Grinberg




Туда Сюда


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


Под спойлером приведен список всех статей этой серии 2018 года.



Примечание 1: Если вы ищете старые версии данного курса, это здесь.


Примечание 2: Если вдруг Вы захотели бы выступить в поддержку моей(Мигеля) работы, или просто не имеете терпения дожидаться статьи неделю, я (Мигель Гринберг)предлагаю полную версию данного руководства(на английском языке) в виде электронной книги или видео. Для получения более подробной информации посетите learn.miguelgrinberg.com.


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


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


Ссылки GitHub для этой главы: Browse, Zip, Diff.


Текущие Ограничения


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


  • Подсистема аутентификации пользователя, которая включает некоторые функции просмотра в app/routes.py, некоторые формы в app/forms.py, некоторые шаблоны в app/templates и поддержку электронной почты в app/email.py.
  • Подсистема ошибок, которая определяет обработчики ошибок в app/errors.py и шаблоны в app/templates.
  • Основные функциональные возможности приложения, которые включают в себя отображение и запись сообщений в блогах, профилей пользователей и последующих событий, а также "живые" переводы сообщений в блогах, которые распределены в большом числе модулей приложений и шаблонов.

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


Один из способов четко увидеть проблему — рассмотреть, как вы начнете второй проект, повторно используя столько, сколько сможете из первого. Например, часть отвечающая за проверку подлинности пользователя должна хорошо работать и в других приложениях. Но если вы хотите использовать этот код как есть, вам придется перейти в несколько модулей и скопировать/вставить соответствующие разделы в новые файлы в новом проекте. Видишь, как это неудобно? Не было бы лучше, если бы этот проект имел все файлы, связанные с аутентификацией, отделенные от остальной части приложения? blueprints особенность Flask помогает достичь более практичной организации, которая упрощает повторное использование кода.


Вторая проблема не столь очевидна. Экземпляр приложения Flask создается как глобальная переменная в app/__init__.py, а затем импортируется множеством модулей приложений. Хотя это само по себе не является проблемой, использование приложения в качестве глобальной переменной может усложнить некоторые сценарии, в частности связанные с тестированием. Представьте, что вы хотите протестировать это приложение в разных конфигурациях. Поскольку приложение определено как глобальная переменная, на самом деле невозможно создать экземпляр двух приложений, использующих разные переменные конфигурации. Другая ситуация, которая не является идеальной, заключается в том, что все тесты используют одно и то же приложение, поэтому тест может вносить изменения в приложение, которые влияют на другой тест, который выполняется позже. В идеале вы хотите, чтобы все тесты выполнялись на первозданном экземпляре приложения.


Фактически вы можете увидеть все это в модуле tests.py. Я прибегаю к трюку изменения конфигурации после того, как она была установлена ​​в приложении, чтобы направить тесты на использование базы данных в памяти вместо базы данных SQLite (по умолчанию) на основе диска. У меня нет другого способа изменить настроенную базу данных, потому что к моменту запуска тестов приложение было уже создано и настроено. Для этой конкретной ситуации изменение конфигурации после того, как она была применена к приложению, кажется, работает нормально, но в других случаях это не поможет, и в любом случае это плохая практика, которая может привести к неясным и трудным для поиска ошибкам.


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


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


Blueprints


В Flask проект представляет собой логическую структуру, которая представляет собой подмножество приложения. Проект может включать такие элементы, как маршруты, функции просмотра, формы, шаблоны и статические файлы. Если вы пишете свой проект в отдельном пакете Python, у вас есть компонент, который инкапсулирует элементы, относящиеся к определенной функции приложения.


Содержимое схемы элементов изначально находится в состоянии покоя. Чтобы связать эти элементы, необходимо зарегистрировать схему элементов в приложении. Во время регистрации все элементы, добавленные в схему элементов, передаются приложению. Таким образом, можно представить схему элементов как временное хранилище для функциональных возможностей приложения, которое помогает организовать код.


Обработка ошибок Blueprint


Первая созданная мной схема элементов была инкапсулирована в поддержку обработчиков ошибок. Структура этой концепции такова:


app/
    errors/                             <-- blueprint пакет
        __init__.py                     <-- blueprint создание
        handlers.py                     <-- error обработчики
    templates/
        errors/                         <-- error шаблоны
            404.html
            500.html
    __init__.py                         <-- blueprint регистрация

В сущности, я переместил модуль app/errors.py в app/errors/handlers.py и два шаблона ошибок в app/templates/errors, чтобы они были отделены от других шаблонов. Мне также пришлось изменить вызовы render_template() в обоих обработчиках ошибок, чтобы использовать подкаталог нового шаблона ошибок. После этого я добавил создание blueprint в модуль app/errors/__init__.py и регистрацию проекта в app/__init__.py после создания экземпляра приложения.


Я должен отметить, что схемы элементов Flask могут быть настроены на отдельный каталог для шаблонов или статических файлов. Я решил переместить шаблоны в подкаталог каталога шаблонов приложения, чтобы все шаблоны находились в одной иерархии, но если вы предпочитаете иметь шаблоны, принадлежащие схеме элементов внутри пакета схемы элементов, это поддерживается. Например, если добавить аргумент template_folder= 'templates' в конструктор Blueprint(), можно сохранить шаблоны схемы элементов в app/errors/templates.


Создание схемы элементов сильно похоже на создание приложения. Это делается в модуле __init__.py пакета blueprint:


app/errors/__init__.py: Errors blueprint.

from flask import Blueprint

bp = Blueprint('errors', __name__)

from app.errors import handlers

Класс Blueprint принимает имя схемы элементов, имя базового модуля (обычно устанавливается в __name__, как в экземпляре приложения Flask), и несколько необязательных аргументов, которые в этом случае мне не нужны. После создания объекта схемы элементов я импортирую handlers.py модуль, чтобы обработчики ошибок в нем были зарегистрированы в схеме элементов. Этот импорт находится внизу, чтобы избежать циклических зависимостей.


В модуле handlers.py вместо прикрепления обработчиков ошибок к приложению с помощью декоратора @app.errorhandler я использую blueprint-декоратор @bp.app_errorhandler. Хотя оба декоратора достигают одного и того же конечного результата, идея состоит в том, чтобы попытаться сделать схему элементов независимой от приложения, чтобы она была более портативной. Мне также нужно изменить путь к двум шаблонам ошибок, чтобы учесть новый подкаталог ошибок, в который они были перемещены.


Последним шагом для завершения рефакторинга обработчиков ошибок является регистрация схемы элементов в приложении:


app/__init__.py: регистрация схемы элементов в приложении.

app = Flask(__name__)

# ...

from app.errors import bp as errors_bp
app.register_blueprint(errors_bp)

# ...

from app import routes, models  # <-- удалите ошибки из этого импорта!

Для регистрации схемы элементов используется метод register_blueprint() экземпляра приложения Flask. При регистрации схемы элементов все функции представления, шаблоны, статические файлы, обработчики ошибок и т. д. подключаются к приложению. Я помещаю импорт схемы элементов прямо над app.register_blueprint(), чтобы избежать циклических зависимостей.


Аутентификация Blueprint


Процесс рефакторинга функций аутентификации приложения в проект довольно похож на процесс обработки обработчиков ошибок. Вот схема рефакторинга:


app/
    auth/                               <-- blueprint пакет
        __init__.py                     <-- blueprint создание
        email.py                        <-- authentication emails
        forms.py                        <-- authentication forms
        routes.py                       <-- authentication routes
    templates/
        auth/                           <-- blueprint шаблоны
            login.html
            register.html
            reset_password_request.html
            reset_password.html
    __init__.py                         <-- blueprint регистрация

Чтобы создать этот проект, мне пришлось перенести все функции, связанные с проверкой подлинности, в новые модули, которые я создал в проекте. Это включает в себя несколько функций просмотра, веб-форм и функций поддержки, таких как функция, которая отправляет маркеры сброса пароля по электронной почте. Я также переместил шаблоны в подкаталог, чтобы отделить их от остальной части приложения, как это было со страницами ошибок.


При определении маршрутов в схеме элементов использует декоратор @bp.route вместо @app.route. Также требуется изменение синтаксиса, используемого в url_for() для создания URL-адресов. Для обычных функций просмотра, прикрепленных непосредственно к приложению, первым аргументом url_for() является имя функции представления. Если маршрут определен в схеме элементов, этот аргумент должен включать имя схемы элементов и имя функции представления, разделенные точкой. Так, например, мне пришлось заменить все вхождения url_for('login') на url_for('auth.логин'), и то же самое для остальных функций представления.


Чтобы зарегистрировать схему элементов auth в приложении, я использовал несколько другой формат:


app/__init__.py: Зарегистрируйте схему элементов проверки подлинности в приложении.

# ...
from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix='/auth')
# ...

Вызов register_blueprint() в этом случае имеет дополнительный аргумент url_prefix. Это совершенно необязательно, но Flask дает вам возможность присоединить схему элементов под префиксом URL, поэтому любые маршруты, определенные в схеме элементов, получают этот префикс в своих URL. Во многих случаях это полезно как своего рода" пространство имен", которое отделяет все маршруты в схеме элементов от других маршрутов в приложении или других схемах элементов. Для аутентификации я подумал, что было бы неплохо иметь все маршруты, начинающиеся с /auth, поэтому я добавил префикс. Таким образом, теперь URL входа будет http://localhost:5000/auth/login. Поскольку я использую url_for() для генерации URL, все URL будут автоматически включать префикс.


Основная схема элементов приложения


Третья схема элементов содержит основную логику приложения. Для рефакторинга этой схемы элементов требуется тот же процесс, что и для предыдущих двух схем элементов. Я дал этой схеме элементов имя main, поэтому все вызовы url_for(), ссылающиеся на функции представления, должны были получить main. префикс. Учитывая, что это основная функциональность приложения, я решил оставить шаблоны в тех же местах. Это не проблема, потому что я переместил шаблоны из двух других схем элементов в подкаталоги.


Шаблон "Фабрика" приложений


Как я уже упоминал во введении к этой главе, наличие приложения в качестве глобальной переменной приводит к некоторым осложнениям, главным образом в виде ограничений для некоторых сценариев тестирования. Прежде чем я представил схемы элементов, приложение должно было быть глобальной переменной, потому что все функции представления и обработчики ошибок должны были быть декорированы функциями, которые находятся в app, такими как @app.route. Но теперь, когда все маршруты и обработчики ошибок были перемещены в схемы элементов, есть гораздо меньше причин, чтобы сохранить приложение глобальным.


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


app/__init__.py: Application factory function.

# ...
db = SQLAlchemy()
migrate = Migrate()
login = LoginManager()
login.login_view = 'auth.login'
login.login_message = _l('Please log in to access this page.')
mail = Mail()
bootstrap = Bootstrap()
moment = Moment()
babel = Babel()

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)

    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    mail.init_app(app)
    bootstrap.init_app(app)
    moment.init_app(app)
    babel.init_app(app)

    # ... нет изменений в blueprint registration

    if not app.debug and not app.testing:
        # ... нет изменений в logging setup

    return app

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


Другие задачи, выполняемые при инициализации, остаются неизменными, но переносятся на factory-функцию вместо того, чтобы находиться в глобальной области. Это включает в себя регистрацию схемы элементов и конфигурацию протоколирования. Обратите внимание, что я добавил условие not app.testing к условию, которое решает, нужно ли включать или отключать ведение журнала электронной почты и файлов, чтобы все эти протоколирования пропускались во время модульных тестов. Флаг app.testing будет True при выполнении модульных тестов из-за того, что переменная TESTING установлена ​​в True в конфигурации.


Итак, кто вызывает функцию фабрики приложений? Очевидным местом для использования этой функции является скрипт верхнего уровня microblog.py, который является единственным модулем, в котором приложение теперь существует в глобальной области. Другое место — в test.py, и я буду обсуждать модульное тестирование более подробно в следующем разделе.


Как я уже упоминал выше, большинство ссылок на приложение ушли с введением схемы элементов, но некоторые из них все еще присутствуют в коде, который я должен рассмотреть. Например, все приложения app/models.py, app/translate.py и app/main/routes.py имеют ссылки на app.config. К счастью, разработчики Flask попытались упростить функции просмотра для доступа к экземпляру приложения без необходимости импортировать его, как я делал до сих пор. Переменная current_app из Flask, представляет собой специальную «контекстную» переменную Flask, которая инициализирует приложение перед отправкой запроса. Вы уже видели другую переменную контекста раньше, g-переменную, в которой я храню текущую локаль. Эти две, вместе с current_user Flask-Login и еще нескольких других, которых вы еще не видели, являются условно «волшебными» переменными, поскольку они работают как глобальные переменные, но доступны только во время обработки запроса и только в потоке, который его обрабатывает.


Замена app переменной current_app в Flask устраняет необходимость импорта экземпляра приложения в качестве глобальной переменной. Я смог заменить все упоминания app.config на current_app.config без каких-либо трудностей с помощью простого поиска и замены.


app/email.py модуль представлял собой несколько большую проблему, поэтому мне пришлось использовать небольшой трюк:


app/email.py: Передача экземпляра приложения другому потоку.

from app import current_app

def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)

def send_email(subject, sender, recipients, text_body, html_body):
    msg = Message(subject, sender=sender, recipients=recipients)
    msg.body = text_body
    msg.html = html_body
    Thread(target=send_async_email,
           args=(current_app._get_current_object(), msg)).start()

В функции send_email() экземпляр приложения передается в качестве аргумента в фоновый поток, который затем доставляет электронное письмо без блокировки основного приложения. Использование current_app непосредственно в функции send_async_email(), которая работает как фоновый поток, не сработало бы, потому что current_app — это переменная контекста, привязанная к потоку, который обрабатывает клиентский запрос. В другом потоке current_app не имеет назначенного значения. Передача current_app напрямую в качестве аргумента для объекта потока тоже не сработала бы, потому что current_app на самом деле является proxy object, который динамически сопоставляется с экземпляром приложения. Таким образом, передача прокси-объекта будет такой же, как использование current_app непосредственно в потоке. Мне нужно было сделать доступ к реальному экземпляру приложения, который хранится внутри прокси-объекта, и передать его в качестве аргумента приложения. Выражение current_app._get_current_object() извлекает фактический экземпляр приложения из объекта прокси, так что это то, что я передал потоку в качестве аргумента.


Еще один "тяжелый случай" — это модуль app/cli.py, который реализует несколько команд быстрого доступа для управления языковыми переводами. В этом случае переменная current_app не работает, поскольку эти команды регистрируются при запуске, а не во время обработки запроса, который является единственным моментом, когда current_app может использоваться. Чтобы удалить ссылку на приложение в этом модуле, я прибегнул к другому трюку, который заключается в перемещении этих пользовательских команд внутри функции register(), которая принимает экземпляр app в качестве аргумента:


app/cli.py: Регистрация пользовательских команд приложения.

import os
import click

def register(app):
    @app.cli.group()
    def translate():
        """Translation and localization commands."""
        pass

    @translate.command()
    @click.argument('lang')
    def init(lang):
        """Initialize a new language."""
        # ...

    @translate.command()
    def update():
        """Update all languages."""
        # ...

    @translate.command()
    def compile():
        """Compile all languages."""
        # ...

Затем я вызвал эту функцию register() из microblog.py. Вот полный microblog.py после рефакторинга:


microblog.py: Основной модуль приложения после рефакторинга.

from app import create_app, db, cli
from app.models import User, Post

app = create_app()
cli.register(app)

@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User, 'Post' :Post}

Unit Testing Improvements


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


Текущая версия tests.py прибегает к хитрости изменения конфигурации после того, как она была применена к экземпляру приложения, что является опасной практикой, поскольку не все типы изменений будут работать, если это сделано в последний момент. Я хочу, чтобы у меня была возможность указать мою конфигурацию тестирования, прежде чем она будет добавлена в приложение.


Функция create_app() теперь принимает класс конфигурации в качестве аргумента. По умолчанию используется класс Config определенный в config.py, но теперь я могу создать экземпляр приложения, который использует другую конфигурацию, просто передав новый класс в функцию фабрики. Вот пример класса конфигурации, который можно использовать для модульных тестов:


tests.py: Конфигурация тестирования.

from config import Config

class TestConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite://'

Здесь я создаю подкласс класса Config приложения и переопределяю конфигурацию SQLAlchemy для использования базы данных SQLite в памяти. Я также добавил атрибут TESTING со значением True, который в настоящее время мне не нужен, но может быть полезен, если приложение должно определить, выполняется ли оно в модульных тестах или нет.


Если вы помните, мои модульные тесты основывались на методах setUp() и tearDown(), которые автоматически вызываются платформой модульного тестирования для создания и уничтожения среды, подходящей для выполнения каждого теста. Теперь я могу использовать эти два метода для создания и уничтожения совершенно нового приложения для каждого теста:


tests.py: Создание приложения для каждого теста.

class UserModelCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app(TestConfig)
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

Новое приложение будет храниться в self.app, но создания приложения недостаточно, чтобы все работало. Рассмотрим инструкцию db.create_all(), которая создает таблицы базы данных. Экземпляр db должен знать, что такое экземпляр приложения, потому что ему нужно получить URI базы данных из app.config, но когда вы работаете с фабрикой приложений, вы на самом деле не ограничены одним приложением, может быть создано гораздо больше одного. Итак, как же db узнает, что должен использовать экземпляр self.app, который я только что создал?


Ответ находится в контексте приложения. Помните переменную current_app, которая каким-то образом выступает в качестве прокси для приложения, когда нет глобального приложения для импорта? Эта переменная ищет активный контекст приложения в текущем потоке, и если она находит его, то получает приложение из него. Если контекст отсутствует, значит невозможно узнать, какое приложение активно, поэтому current_app вызывает исключение. Ниже Вы можете увидеть, как это работает в консоли Python. Это должна быть именно консоль, запущенная с помощью python, потому что команда flask shell, для удобства, автоматически активирует контекст приложения.


>>> from flask import current_app
>>> current_app.config['SQLALCHEMY_DATABASE_URI']
Traceback (most recent call last):
    ...
RuntimeError: Working outside of application context.

>>> from app import create_app
>>> app = create_app()
>>> app.app_context().push()
>>> current_app.config['SQLALCHEMY_DATABASE_URI']
'sqlite:////home/miguel/microblog/app.db'

Вот в чем секрет! Перед вызовом функций просмотра Flask вызывает контекст приложения, который возвращает current_app и g в жизнь. Когда запрос завершен, контекст удаляется вместе с этими переменными. Для вызова db.create_all() для работы с модулем тестирования метода setUp() я выдвинул контекст приложения для только что созданного экземпляра приложения, и таким образом db.create_all() может использовать current_app.config, чтобы узнать, где это база данных. Затем в методе tearDown() я вывожу контекст, чтобы сбросить все в чистое состояние.


Вы также должны знать, что контекст приложения является одним из двух контекстов, которые использует Flask. Существует также request context, что более конкретно, поскольку это относится к запросу. Когда контекст запроса активируется непосредственно перед обработкой запроса, становятся доступными переменные request и session Flask, а также current_user Flask-Login.


Переменные окружения


Как вы возможно заметили, существует ряд вариантов конфигурации, которые зависят от наличия переменных, установленных в вашей среде, до того, как вы запустите сервер. Они включают в себя секретный ключ, информацию о сервере электронной почты, URL-адрес базы данных и ключ API Microsoft Translator. Вы, вероятно, согласитесь со мной в том, что это неудобно, потому что каждый раз, когда вы открываете новый сеанс терминала, эти переменные должны быть установлены снова.


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


Есть пакет Python, который поддерживает .env файлы, называемые python-dotenv. Итак, давайте установим этот пакет:


(venv) $ pip install python-dotenv

Поскольку модуль config.py — это место, где я читаю все переменные среды, то следует импортировать файл .env до создания класса Config, чтобы переменные задавались при построении класса:


config.py: Импорт файла .env в переменные окружения.

import os
from dotenv import load_dotenv

basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))

class Config(object):
    # ...

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


Этот .env-файл можно использовать для всех переменных временной конфигурации, но его нельзя использовать для переменных среды FLASK_APP и FLASK_DEBUG, так как они необходимы уже в процессе начальной загрузки приложения, до того, как экземпляр приложения и его объект конфигурации появится.


В следующем примере показан файл .env, который определяет секретный ключ, настраивает электронную почту для выхода на локально выполняемый почтовый сервер на 25-м порту и без проверки подлинности, устанавливает ключ API Microsoft Translator и устанавливает конфигурацию базы данных для использования значений по умолчанию:


SECRET_KEY=a-really-long-and-unique-key-that-nobody-knows
MAIL_SERVER=localhost
MAIL_PORT=25
MS_TRANSLATOR_KEY=<your-translator-key-here>

Файл Requirements


На данный момент я установил достаточное количество пакетов в виртуальной среде Python. Если вам когда-нибудь понадобится повторно создать среду на другом компьютере, у вас будут проблемы с запоминанием пакетов, которые вы должны были установить, поэтому общепринятой практикой является создание файла requirements.txt в корневой папке проекта с перечислением всех зависимостей и их версий. Создать этот список на самом деле легко:


(venv) $ pip freeze > requirements.txt

Команда pip freeze создаст дамп всех пакетов, установленных в виртуальной среде, в формате, соответствующем файлу requirements.txt. Теперь, если вам нужно создать ту же виртуальную среду на другой машине, вместо установки пакетов по одному, вы можете запустить:


(venv) $ pip install -r requirements.txt

Туда Сюда

Теги:
Хабы:
Всего голосов 6: ↑6 и ↓0+6
Комментарии8

Публикации

Истории

Работа

Data Scientist
78 вакансий
Python разработчик
121 вакансия

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань