Возвращение блудного программиста (ч.3)

от автора

В предыдущих «сериях»

Часть 1

Часть 2

Содержание

Вместо введения

Эта часть будет практической, если помните я решил углубиться в React и Flask. Поэтому при помощи ИИ начинаю. Нет, ИИ не будет делать за меня весь проект, он будет выполнять функцию наставника. Ну и иногда чернорабочего (как, например, собрать файл SQL для инициирования БД) для ускорения ручного труда.

Родилась у меня идея сделать симулятор торговли на Московской бирже. Зачем и почему не спрашивайте – это было давно, просто сейчас дозрело. И, как мне кажется, в качестве учебного проекта вполне подойдёт.

После обсуждения с ИИ самой идеи, пришёл к следующей концепции по её реализации: 

Бэкенд

Flask SQLAlchemy

Фронтенд

React + Vite

СУБД

MySQL

Конечно, по дороге ИИ мне наговорил кучу новых слов типа Zustand, Alembic, TanStack, axios. Доверимся ему, и в ходе написания кода будем смотреть, что и с чем едят.

Решения на стороне сервера, в первую очередь, обусловлены имеющейся инфраструктурой виртуального хостинга reg.ru. Использую то, что есть под рукой в доступе. Начну именно отсюда, так как основные ограничения могут встретиться только здесь. Мало ли, что не встанет.

Итак, приступим

Flask

Сам Flask я уже запускал не reg.ru. Если следовать чётко инструкции, то passenger_wsgi.py и файл проекта лежат в корне. Вроде бы нет проблем, но для деплоя у меня используется GitHub Actions, в частности конкретно за загрузку отвечает SamKirkland/FTP-Deploy-Action. А он чистит целевую папку перед аплоадом, и надо прописывать исключения. При помощи бубна (подсказок ИИ) и метода «научного тыка» (ведь не всё, что говорит ИИ, запускается 100% сразу) получилось перенести весь питон в /app. Еще мне не понравилось, что passenger_wsgi.py содержит имя пользователя в открытом виде (у меня ж всё в GitHub уходит), поэтому захотел вынести в .env.

Вот что получилось:

passenger_wsgi.py (лежит в корне)
import sysimport osdef read_env(path):    if not os.path.exists(path):        return    with open(path) as f:        for line in f:            line = line.strip()            if not line or line.startswith('#') or '=' not in line:                continue            key, val = line.split('=', 1)            value = val.strip().strip("'").strip('"')            os.environ.setdefault(key.strip(), value)read_env(os.path.join(os.path.dirname(__file__), '.env'))INTERP = os.getenv('PYTHON_INTERP')if sys.executable != INTERP:   os.execl(INTERP, INTERP, *sys.argv)sys.path.append(os.getcwd())from app import create_appapplication = create_app()if __name__ == "__main__":   application.run(host='0.0.0.0')

Кстати, функцию read_env() пришлось заставлять писать ИИ (я совсем пока не крут в python). Но интересно другое: сперва он мне предложил сделать через dotenv, но надо убедиться, что он есть до загрузки моего окружения. Конечно, его нет по умолчанию. Тогда он начал предлагать разные костыли, чтоб не грузить файл в GitHub и сохранить имя пользователя в нём. Пришлось напомнить, что раз .env есть, то можно его и прочитать. Собственно вот.

Окружение .env
PYTHON_INTERP=/flaskenv/bin/pythonDB_URL=mysql+mysqldb://dbuser:dbpass@localhost/dbnameJWT_SECRET=SECRETALLOWED_ORIGIN=https://HOST.ru

Остальные строки уже на будущее

__init__.py (лежит в /app)
import osfrom flask import Flaskdef create_app():    app = Flask(__name__)    @app.route('/api/ping')    def ping():        return {'status': 'ok'}    return app

Ну тут пока всё упростил до минимума, чтобы проверить запустится или нет.

Кстати, пока ковырялся, подправил скрипт для деплоя, чтобы он всегда закидывал .restart-app (файл нужен для перезапуска Flask через passenger_wsgi).

Всё, Flask на поддомене заработал. Можно приступать к дальнейшим шагам.

Python окружение

Подключился по SSH к серверу, и по совету ИИ, установил следующее в своё окружение.

Запуск окружения
source flaskenv/bin/activate

Делал всё по одному, для наглядности. Не до конца понимаю, что и в какой момент я буду использовать, и как это будет выглядеть в коде. Хотя в целом общее представление есть.

Установка пакетов
pip install flask-sqlalchemypip install flask-migratepip install flask-jwt-extendedpip install mysqlclientpip install requestspip install python-dotenvpip install bcryptpip install marshmallowpip install flask-cors

Ну и под конец.

Сохраняю requirements.txt
pip freeze > requirements.txt

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

MySQL

Ну с MySQL всё намного проще. Во-первых, она уже есть на хостинге, к ней также стоит phpMyAdmin. А, во-вторых, с MySQL я знаком давно и достаточно неплохо, что-то, возможно, и не помню, но это мелочи.

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

Последние штрихи

После успешного выполнения всего описанного повторил это на локальной машине, с той лишь разницей, что пришлось сделать еще один файлик run.py для локального запуска.

run.py (только локально)
import sysimport osfrom app import create_appapplication = create_app()if __name__ == "__main__":   application.run(host='0.0.0.0')

Залил всё на GitHub. Сделал деплой (по аналогии с прошлым проектом). И… снёс прошлый проект на сервере. Нужно внимательнее делать копипаст, но ничего всё восстановил, переписал. И… вуаля. Теперь всё работает.

Вместо заключения

Выводы какие-то делать ещё рано. Пока только готова инфраструктура для backend-разработки, которая соответствует задуманной концепции. Связь ПК-> GitHub -> сервер выстроена, так что можно писать сам код.

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

P.S.: Кто знает где найти логи python на shared reg.ru? Всё облазил — не сообразил. Просто, когда python падает приходится не по логам искать, а методом выбрасывания кусков кода.

ссылка на оригинал статьи https://habr.com/ru/articles/1029736/