# Backend с использованием Flask Инстукция содержит туториал и видеопример развертывания Flask приложения. ## Видеопример - Деплой Flask приложения с базой данных PostgreSQL
Содержание видео Таймкоды: - 00:00 Intro - 00:20 Развертывание БД - 01:00 Подключение к БД - 02:02 Развертывание сайта - 02:27 requirements.txt - 03:00 amvera.yml - 03:48 Загрузка файлов - 04:24 Переменные окружения - 05:24 Постоянное хранилище /data - 05:42 Подключение через Git
```{eval-rst} .. youtube:: i_DN7LK1MRw :align: center :width: 100% ``` ## Планирование Создадим простое приложение на языке программирования Python с использованием фреймворка Flask. Это будет API для TODO-заметок. ```{eval-rst} .. admonition:: Подсказка :class: hint Для ознакомления с принципом работы с git рекомендуем `эту статью `_, которая позволит понять, как создавать git-репозиторий и вносить в него изменения. ``` Каждая заметка будет определяться следующим образом: ``` { "text": "Купить молоко", "done": true } ``` Все заметки будут храниться массивом в файле формата JSON. Определим API следующим образом. - `GET /todo` получает список всех TODO. - `GET /todo/` получает TODO с заданным id (индексом в массиве). - `POST /todo` добавляет новую TODO в конец списка. - `PUT /todo/` заменяет TODO с заданным id. ## Реализация Для начала напишем само приложение. Так как наше приложение использует Flask, создадим файл `requirements.txt`: ```text Flask==2.2.2 Flask-CORS==3.0.10 gunicorn==20.1.0 ``` Напишем код приложения, работающий локально, в файле `app.py`: ```python import json from flask import Flask, request, abort from flask_cors import CORS FILENAME = "todo.json" def get_data(): try: with open(FILENAME, "r", encoding="utf-8") as f: return json.load(f) except FileNotFoundError: return [] def save_data(data): with open(FILENAME, "w", encoding="utf-8") as f: json.dump(data, f) app = Flask(__name__) cors = CORS(app) @app.route("/") def index(): return "TODO App" @app.route("/todo") def get_all_todo(): return get_data() @app.route("/todo/") def get_single_todo(id): data = get_data() if id < 0 or id >= len(data): abort(404) return data[id] @app.route("/todo", methods=["POST"]) def add_new_todo(): new_todo = request.json if new_todo is None: abort(400) data = get_data() data.append(new_todo) save_data(data) return "OK", 201 @app.route("/todo/", methods=["PUT"]) def update_todo(id): data = get_data() if id < 0 or id >= len(data): abort(404) updated_todo = request.json if updated_todo is None: abort(400) data[id] = updated_todo save_data(data) return "OK" if __name__ == "__main__": app.run(port=8080) ``` Установим зависимости: ```shell pip install -r requirements.txt ``` Запустим приложение: ```shell python app.py ``` Убедимся в его работоспособности при помощи Postman. ## Подготовка приложения для Amvera Подготовим приложение для окружения Amvera. Напишем `amvera.yaml`. Написать yaml файл можно как самостоятельно, используя инструкцию ниже, так и воспользоваться нашим генератором yaml, перейдя по [ссылке](https://manifest.amvera.ru/). ```yaml meta: environment: python toolchain: name: pip run: command: gunicorn --bind 0.0.0.0:5000 app:app containerPort: 5000 ``` Так как для развертывания нашего приложения мы используем gunicorn, добавим его в `requirements.txt`: ```text Flask==2.2.2 Flask-CORS==3.0.10 gunicorn==20.1.0 ``` Осталось решить проблему с сохранением файла с TODO в /data. Добавим `import os` в начало файла `app.py`, а также изменим объявление переменной `FILENAME`: ```python FILENAME = "/data/todo.json" if "AMVERA" in os.environ else "todo.json" ``` Теперь начало файла `app.py` выглядит так: ```python import os import json from flask import Flask, request, abort from flask_cors import CORS FILENAME = "/data/todo.json" if "AMVERA" in os.environ else "todo.json" def get_data(): # ... остаток файла ``` Инициализируем репозиторий git: ```shell git init ``` Добавим созданные нами файлы в индекс: ```shell git add app.py requirements.txt amvera.yml ``` Зафиксируем изменения: ```shell git commit -m "TODO App" ``` ## Создание проекта в Amvera Теперь нужно создать приложение в Amvera. Откроем страницу [https://cloud.amvera.ru/projects](https://cloud.amvera.ru/projects) и нажмите кнопку "Создать". В процессе создания приложение, возьмем данные для git репозитория на этапе "Загрузка данных". Так же их можно найти во вкладке "Репозиторий" самого приложения. ```shell git remote add amvera https://git.amvera.ru/<имя-пользователя>/todo-app git push amvera master ``` ![config](../../img/quick-start2.png) После этого начнется [сборка](../../applications/build.md) и [развертывание](../../applications/run.md) приложения. Дождитесь появления статуса "Успешно развернуто". ## Проверка работоспособности Снова воспользуемся Postman для отправки запросов. Для проверки сохранности данных после перезагрузки, перезапустим сервис кнопкой в интерфейсе. После завершения перезапуска отправим запрос Постманом для проверки наличия TODO заметок. Если они есть, значит мы все сделали правильно. Если что-то не работает, рекомендуем ознакомиться с логами Сборки и Приложения. ```{eval-rst} .. admonition:: Подсказка :class: hint Если логи пишутся в print, для их отображения надо выставить переменную окружения `PYTHONUNBUFFERED` в 1. ``` ```{eval-rst} .. admonition:: Важно :class: warning Сохраняйте файлы БД и иные изменяемые данные в постоянное хранилище, чтобы избежать их потери при обновлении проекта, когда производится "откат" папки код до состояния обновления репозитория. Папка data в корне проекта и директория /data, это разные директории. Проверить, что сохранение идет в /data, можно зайдя в папку "data" на странице "Репозиторий". ``` ```{eval-rst} .. admonition:: Важно :class: warning Чтобы избежать ошибки 502, измените в вашем коде host 127.0.0.1 (или подобный localhost) на 0.0.0.0, и пропишите в конфигурации порт, который слушает ваше приложение (пример - 8080). ``` Поздравляем, вы успешно создали свое первое приложение в Amvera! ## Если у вас не получается развернуть проект Напишите наблюдаемую вами симптоматику на support@amvera.ru с указанием вашего имени пользователя и названия проекта, мы постараемся вам помочь.