Yandex Cloud
Поиск
Связаться с намиПодключиться
  • Документация
  • Блог
  • Все сервисы
  • Статус работы сервисов
    • Популярные
    • Инфраструктура и сеть
    • Платформа данных
    • Контейнеры
    • Инструменты разработчика
    • Бессерверные вычисления
    • Безопасность
    • Мониторинг и управление ресурсами
    • Машинное обучение
    • Бизнес-инструменты
  • Все решения
    • По отраслям
    • По типу задач
    • Экономика платформы
    • Безопасность
    • Техническая поддержка
    • Каталог партнёров
    • Обучение и сертификация
    • Облако для стартапов
    • Облако для крупного бизнеса
    • Центр технологий для общества
    • Облако для интеграторов
    • Поддержка IT-бизнеса
    • Облако для фрилансеров
    • Обучение и сертификация
    • Блог
    • Документация
    • Контент-программа
    • Мероприятия и вебинары
    • Контакты, чаты и сообщества
    • Идеи
    • Истории успеха
    • Тарифы Yandex Cloud
    • Промоакции и free tier
    • Правила тарификации
  • Документация
  • Блог
Проект Яндекса
© 2025 ООО «Яндекс.Облако»
Yandex Compute Cloud
  • Yandex Container Solution
    • Все руководства
    • Настройка синхронизации часов с помощью NTP
    • Автомасштабирование группы ВМ для обработки сообщений из очереди
    • Обновление группы ВМ под нагрузкой
    • Развертывание Remote Desktop Gateway
    • Начало работы с Packer
    • Передача логов с ВМ в Yandex Cloud Logging
    • Сборка образа ВМ с набором инфраструктурных инструментов с помощью Packer
    • Миграция в Yandex Cloud с помощью Хайстекс Акура
    • Защита от сбоев с помощью Хайстекс Акура
    • Резервное копирование ВМ с помощью Хайстекс Акура
    • Развертывание отказоустойчивой архитектуры с прерываемыми ВМ
    • Настройка отказоустойчивой архитектуры в Yandex Cloud
    • Создание триггера для бюджетов, который вызывает функцию для остановки ВМ
    • Создание триггеров, которые вызывают функции для остановки ВМ и отправки уведомлений в Telegram
    • Создание веб-приложения на Python с использованием фреймворка Flask
    • Создание SAP-программы в Yandex Cloud
    • Развертывание сервера Minecraft в Yandex Cloud
    • Автоматизация сборки образов с помощью Jenkins и Packer
    • Создание тестовых виртуальных машин через GitLab CI
    • Высокопроизводительные вычисления (HPC) на прерываемых ВМ
    • Настройка SFTP-сервера на Centos 7
    • Развертывание параллельной файловой системы GlusterFS в высокодоступном режиме
    • Развертывание параллельной файловой системы GlusterFS в высокопроизводительном режиме
    • Резервное копирование в Object Storage с помощью Bacula
    • Построение пайплайна CI/CD в GitLab с использованием serverless-продуктов
    • Реализация защищенной высокодоступной сетевой инфраструктуры с выделением DMZ на основе Check Point NGFW
    • Сегментация облачной инфраструктуры с помощью решения Check Point Next-Generation Firewall
    • Настройка защищенного туннеля GRE поверх IPsec
    • Создание бастионного хоста
    • Реализация отказоустойчивых сценариев для сетевых виртуальных машин
    • Создание туннеля между двумя подсетями при помощи OpenVPN Access Server
    • Создание внешней таблицы на базе таблицы из бакета Object Storage с помощью конфигурационного файла
    • Настройка сетевой связности между подсетями BareMetal и Virtual Private Cloud
    • Работа со снапшотами в Managed Service for Kubernetes
    • Запуск языковой модели DeepSeek-R1 в кластере GPU
    • Запуск библиотеки vLLM с языковой моделью Gemma 3 на ВМ с GPU
    • Доставка USB-устройств на виртуальную машину или сервер BareMetal
  • Управление доступом
  • Справочник Terraform
  • Метрики Monitoring
  • Аудитные логи Audit Trails
  • История изменений
  • Обучающие курсы

В этой статье:

  • Перед началом работы
  • Создайте облачную сеть и подсеть
  • Создайте группу безопасности
  • Создайте и настройте виртуальную машину
  • Создайте и запустите приложение
  • Создайте и настройте шаблоны HTML
  • Создайте HTML-шаблон главной страницы
  • Настройте другие шаблоны HTML
  • Настройте базу данных
  • Настройте отображение постов блога
  • Отобразите список всех постов блога
  • Отобразите отдельные посты блога
  • Добавьте действия с постами
  • Создание нового поста
  • Редактирование существующего поста
  • Удаление поста
  • Подведите итоги
  • Удалите созданные ресурсы
  1. Практические руководства
  2. Создание веб-приложения на Python с использованием фреймворка Flask

Создание веб-приложения на Python с использованием фреймворка Flask

Статья создана
Yandex Cloud
Обновлена 6 марта 2025 г.
  • Перед началом работы
    • Создайте облачную сеть и подсеть
    • Создайте группу безопасности
  • Создайте и настройте виртуальную машину
  • Создайте и запустите приложение
  • Создайте и настройте шаблоны HTML
    • Создайте HTML-шаблон главной страницы
    • Настройте другие шаблоны HTML
  • Настройте базу данных
  • Настройте отображение постов блога
    • Отобразите список всех постов блога
    • Отобразите отдельные посты блога
  • Добавьте действия с постами
    • Создание нового поста
    • Редактирование существующего поста
    • Удаление поста
  • Подведите итоги
  • Удалите созданные ресурсы

С помощью этого руководства вы с нуля разработаете простое веб-приложение — мини-блог, в котором пользователь сможет создавать, просматривать, редактировать и удалять посты. Для этого в Yandex Cloud вы создадите виртуальную машину Yandex Compute Cloud и запустите на ней веб-сервер.

Веб-приложение написано на языке Python и использует СУБД SQLite для работы с данными. Визуальное оформление выполнено с помощью инструментария Bootstrap.

В качестве основы приложения выбран фреймворк Flask, как один из наиболее доступных фреймворков для создания веб-приложений на языке программирования Python.

Фреймворк Flask позволяет создавать веб-приложения c использованием всего одного файла с исходным кодом, не требует соблюдения какой-то определенной структуры директорий или написания сложного шаблонного кода перед началом использования. Также Flask предоставляет набор инструментов Werkzeug и механизм шаблонов Jinja для динамического создания HTML-страниц.

Исходный код приложения доступен в файле flask_blog.zip.

Схема работы создаваемого веб-приложения:

  1. HTTP-запросы поступают из браузера пользователя на виртуальную машину под управлением ОС Linux Ubuntu с установленным и запущенным веб-сервером Flask.
  2. Веб-сервер передает запрос в веб-приложение, маршрутизатор которого в зависимости от URL запроса вызывает подходящую функцию-обработчик.
  3. Функция-обработчик выполняет запрос к базе данных SQLite, чтобы получить или записать необходимые данные.
  4. Полученные из БД данные функция передает в подходящий HTML-шаблон Jinja, который возвращает готовый HTML-код страницы.
  5. Полученный HTML-код передается на веб-сервер, который в свою очередь передает этот код в браузер пользователя.

Чтобы создать веб-приложение с помощью Flask:

  1. Подготовьте облако к работе.
  2. Создайте и настройте виртуальную машину.
  3. Создайте и запустите приложение.
  4. Создайте и настройте шаблоны HTML.
  5. Настройте базу данных.
  6. Настройте отображение постов блога.
  7. Добавьте действия с постами.
  8. Подведите итоги.

Если веб-приложение вам больше не нужно, удалите все используемые им ресурсы.

Перед началом работыПеред началом работы

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

В стоимость поддержки создаваемого приложения входят:

  • плата за использование публичного IP-адреса (см. тарифы Yandex Virtual Private Cloud);
  • плата за вычислительные ресурсы и диски ВМ (см. тарифы Yandex Compute Cloud).

Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:

  1. Перейдите в консоль управления, затем войдите в Yandex Cloud или зарегистрируйтесь.
  2. На странице Yandex Cloud Billing убедитесь, что у вас подключен платежный аккаунт, и он находится в статусе ACTIVE или TRIAL_ACTIVE. Если платежного аккаунта нет, создайте его и привяжите к нему облако.

Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака.

Подробнее об облаках и каталогах.

Создайте облачную сеть и подсетьСоздайте облачную сеть и подсеть

В Yandex Cloud ресурсы связаны между собой и с интернетом при помощи облачных сетей, в которых ресурсам выделяются публичные IP-адреса, а также диапазоны внутренних IP-адресов или подсети.

Чтобы создать виртуальную сеть и подсеть для вашего веб-сервера:

Консоль управления
CLI
API
  1. В консоли управления выберите ваш каталог.
  2. В списке сервисов выберите Virtual Private Cloud.
  3. Справа сверху нажмите кнопку Создать сеть.
  4. В поле Имя укажите webserver-network.
  5. В поле Дополнительно отключите опцию Создать подсети.
  6. Нажмите кнопку Создать сеть.
  7. На панели слева выберите Подсети.
  8. Справа сверху нажмите Создать.
  9. В поле Имя укажите webserver-subnet-ru-central1-b.
  10. В поле Зона выберите зону доступности ru-central1-b.
  11. В поле Сеть выберите облачную сеть webserver-network.
  12. В поле CIDR укажите 192.168.1.0/24.
  13. Нажмите кнопку Создать подсеть.
  1. Создайте сеть webserver-network:

    yc vpc network create webserver-network
    

    Результат:

    id: enp1gg8kr3pv********
    folder_id: b1gt6g8ht345********
    created_at: "2023-12-20T20:08:11Z"
    name: webserver-network
    default_security_group_id: enppne4l2eg5********
    

    Подробнее о команде yc vpc network create читайте в справочнике CLI.

  2. Создайте подсеть в зоне доступности ru-central1-b:

    yc vpc subnet create webserver-subnet-ru-central1-b \
      --zone ru-central1-b \
      --network-name webserver-network \
      --range 192.168.1.0/24
    

    Результат:

    id: e2li9tcgi7ii********
    folder_id: b1gt6g8ht345********
    created_at: "2023-12-20T20:11:16Z"
    name: webserver-subnet-ru-central1-b
    network_id: enp1gg8kr3pv********
    zone_id: ru-central1-b
    v4_cidr_blocks:
      - 192.168.1.0/24
    

    Подробнее о команде yc vpc subnet create читайте в справочнике CLI.

  1. Чтобы создать сеть, воспользуйтесь методом REST API create для ресурса Network или вызовом gRPC API NetworkService/Create.

  2. Чтобы создать подсеть, воспользуйтесь методом REST API create для ресурса Subnet или вызовом gRPC API SubnetService/Create.

Создайте группу безопасностиСоздайте группу безопасности

Группы безопасности служат основным механизмом разграничения сетевого доступа в Yandex Cloud и помогают ограничить для облачных ресурсов несанкционированный трафик на уровне облачной сети.

Создайте группу безопасности, разрешающую входящий TCP-трафик для портов 5000 и 22, а также любой исходящий трафик. Порт 22 необходим для подключения к ВМ по SSH, порт 5000 по умолчанию используется для работы веб-сервера Flask.

Консоль управления
CLI
API
  1. В консоли управления выберите ваш каталог.

  2. В списке сервисов выберите Virtual Private Cloud.

  3. На панели слева выберите Группы безопасности.

  4. Нажмите кнопку Создать группу безопасности.

  5. В поле Имя укажите имя webserver-sg.

  6. В поле Сеть выберите созданную ранее сеть webserver-network.

  7. В блоке Правила создайте следующие правила для управления трафиком:

    Направление
    трафика
    Описание Диапазон портов Протокол Источник /
    Назначение
    CIDR блоки
    Входящий Flask 5000 TCP CIDR 0.0.0.0/0
    Входящий ssh 22 TCP CIDR 0.0.0.0/0
    Исходящий any Весь Любой CIDR 0.0.0.0/0
  8. Нажмите кнопку Сохранить.

Выполните команду:

yc vpc security-group create \
  --name webserver-sg \
  --rule "description=Flask,direction=ingress,port=5000,protocol=tcp,v4-cidrs=[0.0.0.0/0]" \
  --rule "description=ssh,direction=ingress,port=22,protocol=tcp,v4-cidrs=[0.0.0.0/0]" \
  --rule "description=any,direction=egress,port=any,protocol=any,v4-cidrs=[0.0.0.0/0]" \
  --network-name webserver-network

Результат:

id: enpv1c0q7j01********
folder_id: b1gt6g8ht345********
created_at: "2024-03-23T18:54:05Z"
name: webserver-sg
network_id: enp9mji1m7b3********
status: ACTIVE
rules:
  - id: enpmbsk2hjfd********
    description: Flask
    direction: INGRESS
    ports:
      from_port: "5000"
      to_port: "5000"
    protocol_name: TCP
    protocol_number: "6"
    cidr_blocks:
      v4_cidr_blocks:
        - 0.0.0.0/0
  - id: enpna5id9265********
    description: ssh
    direction: INGRESS
    ports:
      from_port: "22"
      to_port: "22"
    protocol_name: TCP
    protocol_number: "6"
    cidr_blocks:
      v4_cidr_blocks:
        - 0.0.0.0/0
  - id: enpen3vf7rui********
    description: any
    direction: EGRESS
    protocol_name: ANY
    protocol_number: "-1"
    cidr_blocks:
      v4_cidr_blocks:
        - 0.0.0.0/0

Сохраните полученный идентификатор (id) группы безопасности: он потребуется при создании виртуальной машины.

Подробнее о команде yc vpc security-group create читайте в справочнике CLI.

Чтобы создать группу безопасности, воспользуйтесь методом REST API create для ресурса SecurityGroup или вызовом gRPC API SecurityGroupService/Create.

Создайте и настройте виртуальную машинуСоздайте и настройте виртуальную машину

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

Создаваемое веб-приложение будет развернуто на виртуальной машине под управлением OC Linux Ubuntu 22.04 LTS.

  1. Создайте виртуальную машину:

    Перед тем как приступать к созданию виртуальной машины, подготовьте пару ключей (открытый и закрытый) для доступа к ВМ по SSH.

    Консоль управления
    CLI
    API
    1. В консоли управления выберите каталог, в котором будет создана ВМ.

    2. В списке сервисов выберите Compute Cloud.

    3. На панели слева выберите Виртуальные машины.

    4. Нажмите кнопку Создать виртуальную машину.

    5. В блоке Образ загрузочного диска выберите образ Ubuntu 22.04 LTS.

    6. В блоке Расположение выберите зону доступности ru-central1-b.

    7. В блоке Сетевые настройки:

      • В поле Подсеть выберите созданную ранее подсеть webserver-subnet-ru-central1-b.
      • В поле Публичный IP-адрес выберите Автоматически.
      • В поле Группы безопасности выберите созданную ранее группу безопасности webserver-sg.
    8. В блоке Доступ выберите SSH-ключ и укажите данные для доступа к ВМ:

      • В поле Логин укажите имя пользователя yc-user.
      • В поле SSH-ключ выберите SSH-ключ, сохраненный в вашем профиле пользователя организации.

        Если в вашем профиле нет сохраненных SSH-ключей или вы хотите добавить новый ключ:

        • Нажмите кнопку Добавить ключ.
        • Задайте имя SSH-ключа.
        • Загрузите или вставьте содержимое открытого SSH-ключа. Пару SSH-ключей для подключения к ВМ по SSH необходимо создать самостоятельно.
        • Нажмите кнопку Добавить.

        SSH-ключ будет добавлен в ваш профиль пользователя организации.

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

    9. В блоке Общая информация задайте имя ВМ: mywebserver.

    10. Нажмите кнопку Создать ВМ.

    Выполните команду, указав сохраненный на предыдущем шаге идентификатор группы безопасности:

    yc compute instance create \
      --name mywebserver \
      --zone ru-central1-b \
      --network-interface subnet-name=webserver-subnet-ru-central1-b,nat-ip-version=ipv4,security-group-ids=<идентификатор_группы_безопасности> \
      --create-boot-disk image-folder-id=standard-images,image-id=fd8ne6e3etbrr2ve9nlc \
      --ssh-key <файл_открытого_SSH-ключа>
    

    Где --ssh-key — путь к файлу с открытым SSH-ключом. Например, ~/.ssh/id_ed25519.pub.

    Результат:

    done (32s)
    id: epdv79cu67np********
    folder_id: b1gt6g8ht345********
    created_at: "2024-03-23T19:17:09Z"
    name: mywebserver
    zone_id: ru-central1-b
    platform_id: standard-v2
    resources:
      memory: "2147483648"
      cores: "2"
      core_fraction: "100"
    status: RUNNING
    metadata_options:
      gce_http_endpoint: ENABLED
      aws_v1_http_endpoint: ENABLED
      gce_http_token: ENABLED
      aws_v1_http_token: DISABLED
    boot_disk:
      mode: READ_WRITE
      device_name: epdg0926k12t********
      auto_delete: true
      disk_id: epdg0926k12t********
    network_interfaces:
      - index: "0"
        mac_address: d0:0d:1f:3a:59:e3
        subnet_id: e2l3qffk0h6t********
        primary_v4_address:
          address: 192.168.1.14
          one_to_one_nat:
            address: 62.84.***.***
            ip_version: IPV4
        security_group_ids:
          - enpv1c0q7j01********
    serial_port_settings:
      ssh_authorization: INSTANCE_METADATA
    gpu_settings: {}
    fqdn: epdv79cu67np********.auto.internal
    scheduling_policy: {}
    network_settings:
      type: STANDARD
    placement_policy: {}
    

    Подробнее о команде yc compute instance create читайте в справочнике CLI.

    Чтобы создать виртуальную машину, воспользуйтесь методом REST API create для ресурса Instance или вызовом gRPC API InstanceService/Create.

    В вашем каталоге будет создана виртуальная машина mywebserver. Чтобы подключиться к ней по SSH, используйте логин yc-user и публичный IP-адрес этой ВМ. Если вы планируете пользоваться созданным веб-сервером длительное время, сделайте публичный IP-адрес этой ВМ статическим.

  2. Создайте и активируйте виртуальное окружение.

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

    Для создания виртуального окружения используйте модуль библиотеки Python 3 venv:

    1. Подключитесь к виртуальной машине mywebserver.

    2. В директории текущего пользователя создайте поддиректорию проекта flask_blog, в которой будет находиться приложение, и перейдите в нее:

      mkdir flask_blog && cd flask_blog
      
    3. Установите модуль создания виртуальных сред venv:

      sudo apt install python3.10-venv
      
    4. Создайте виртуальное окружение env:

      python3 -m venv env
      
    5. Активируйте созданное виртуальное окружение:

      source env/bin/activate
      

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

      (env) yc-user@ubuntu:~/flask_blog$
      

      Примечание

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

      Чтобы деактивировать виртуальное окружение, выполните команду:

      deactivate
      
  3. Установите Flask:

    pip install flask
    

    Результат:

    Successfully installed Jinja2-3.1.3 MarkupSafe-2.1.5 Werkzeug-3.0.1 blinker-1.7.0 click-8.1.7 flask-3.0.2 itsdangerous-2.1.2
    

Создайте и запустите приложениеСоздайте и запустите приложение

Создайте простое веб-приложение внутри файла Python и запустите его для начала работы сервера.

  1. В директории проекта flask_blog создайте и откройте файл app.py:

    nano app.py
    

    Этот файл позволяет понять, как приложение будет обрабатывать HTTP-запросы.

  2. Добавьте в файл следующий код:

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def hello():
        return 'Hello, World!'
    

    В этом коде:

    • Импортируется объект Flask из пакета flask.
    • Создается экземпляр приложения Flask с именем app. Специальная переменная __name__ содержит имя текущего модуля Python и указывает экземпляру его расположение. Это необходимо, так как Flask устанавливает ряд путей внутри приложения.
    • С помощью декоратора @app.route('/') функция Python превращается в функцию визуализации Flask. Функция визуализации конвертирует возвращаемое значение в HTTP-ответ, который может быть обработан HTTP-клиентом, таким как веб-браузер. Значение '/' в маршруте @app.route()​​ устанавливает, что эта функция будет отвечать на веб-запросы для URL /, который является основным URL-адресом веб-приложения.
    • Создается функция hello(), которая возвращает строку Hello, World! в качестве ответа.

    Сохраните и закройте файл app.py.

  3. Задайте переменные окружения Flask:

    export FLASK_APP=app && export FLASK_DEBUG=true
    

    Где:

    • FLASK_APP=app указывает на расположение приложения — файла app.py.
    • FLASK_DEBUG=true указывает, что приложение необходимо запустить в режиме разработки.
  4. Запустите приложение:

    flask run \
      --host=0.0.0.0
    

    Где параметр --host=0.0.0.0 запускает сервер приложения на всех IP-адресах виртуальной машины. Не указав этот параметр, вы не сможете обратиться к приложению по публичному IP-адресу ВМ.

    Результат:

     * Serving Flask app 'app'
     * Debug mode: on
    WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
     * Running on all addresses (0.0.0.0)
     * Running on http://127.0.0.1:5000
     * Running on http://192.168.1.14:5000
    Press CTRL+C to quit
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 884-371-***
    

    Для нас важна следующая информация:

    • Название работающего приложения — app.
    • Debug mode: on означает, что отладчик Flask работает. Эта функция полезна при разработке, так как при возникновении проблем она выдает детализированные сообщения об ошибках, что упрощает работу по их устранению.
    • Приложение работает на всех адресах.

    Вы можете изменить порт веб-сервера на любое другое значение, задав дополнительный параметр --port=<номер_порта>. Например, команда flask run --host=0.0.0.0 --port=5001 запустит веб-сервер Flask на порте 5001.

  5. Откройте браузер и введите в адресной строке http://<публичный_IP-адрес_ВМ>:5000/​.

    Браузер отобразит строку Hello, World!. Это подтверждает, что ваше приложение успешно работает и к нему можно обращаться из интернета.

Важно

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

Оставьте веб-сервер работать в текущем окне и откройте новое окно терминала. В новом окне терминала вы будете выполнять дальнейшую настройку приложения. Это позволит проверять работу внесенных в приложение изменений без необходимости постоянно останавливать и вновь запускать веб-сервер.

При открытии нового окна терминала:

  1. Подключитесь к ВМ.

  2. Перейдите в директорию проекта flask_blog, активируйте виртуальное окружение и задайте переменные окружения:

    cd flask_blog && source env/bin/activate && export FLASK_APP=app && export FLASK_DEBUG=true
    

Создайте и настройте шаблоны HTMLСоздайте и настройте шаблоны HTML

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

Использовать в приложении шаблоны позволяет вспомогательная функция render_template(), в которой доступен механизм шаблонов Jinja. Вы будете использовать шаблоны HTML для создания страниц приложения: главной страницы со списком текущих постов блога и страниц создания, просмотра и редактирования поста.

Создайте HTML-шаблон главной страницыСоздайте HTML-шаблон главной страницы

  1. Откройте файл app.py:

    nano app.py
    
  2. Замените содержимое файла следующим кодом:

    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return render_template('index.html')
    

    В этом коде:

    • Дополнительно импортируется вспомогательная функция render_template(), которая позволяет визуализировать шаблоны HTML.
    • Функция hello() заменяется на функцию index(), которая возвращает результат вызова вспомогательной функции render_template() с аргументом index.html​​. Аргумент функции указывает на файл шаблона, который должен быть расположен в директории шаблонов и который будет использован для визуализации.

    Примечание

    Директория шаблонов и файл index.html еще не созданы. Вы получите сообщение об ошибке, если запустите приложение на этом этапе.

    Сохраните и закройте файл app.py.

  3. В директории проекта flask_blog создайте поддиректорию templates:

    mkdir templates
    
  4. Создайте и откройте файл-шаблон index.html в директории templates:

    nano templates/index.html
    
  5. Добавьте в файл index.html следующий код:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>FlaskBlog</title>
      </head>
      <body>
        <h1>Welcome to FlaskBlog</h1>
      </body>
    </html>
    

    В этом HTML коде описана простейшая страница с заголовком FlaskBlog и содержимым Welcome to FlaskBlog в заголовке первого уровня.

    Сохраните и закройте файл index.html.

  6. Обновите главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000/.

    Результат:

    Welcome to FlaskBlog.
    
  7. Кроме директории templates в веб-приложениях Flask обычно также существует директория static, в которой сохраняются статичные файлы, такие как стили CSS, скрипты JavaScript или изображения, используемые приложением.

    В директории проекта flask_blog создайте поддиректорию static:

    mkdir static
    
  8. Во вновь созданной директории static создайте поддиректорию css:

    mkdir static/css
    

    Примечание

    Вложенные директории используются для упорядочивания статичных файлов в специальных папках. Например, файлы JavaScript обычно хранятся в директории js, изображения — в директории images (или img) и т.д.

  9. Создайте и откройте файл style.css в директории static/css:

    nano static/css/style.css
    
  10. Добавьте в файл style.css следующий код:

    h1 {
        border: 2px #eee solid;
        color: #fc3d17;
        text-align: center;
        padding: 10px;
    }
    

    Этот CSS-код изменяет оформление текста внутри HTML-тегов <h1>:

    • свойство border добавляет границу;
    • свойство color изменяет цвет текста;
    • свойство text-align выравнивает текст;
    • свойство padding добавляет отступ до текста от границы элемента.

    Сохраните и закройте файл style.css.

  11. Откройте шаблон index.html:

    nano templates/index.html
    
  12. Добавьте ссылку на файл style.css внутри раздела <head>:

    ...
      <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="{{ url_for('static', filename= 'css/style.css') }}">
        <title>FlaskBlog</title>
      </head>
    ...
    

    В ссылке используется вспомогательная функция url_for()​​​ для генерации адреса расположения файла. Первый аргумент указывает на то, что ссылка связана со статическими файлами, второй аргумент — путь к файлу в поддиректории статических файлов.

    Сохраните и закройте файл index.html.

  13. Обновите главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000/.

    Вы увидите, что цвет текста Welcome to FlaskBlog​​​ теперь красный и расположен по центру внутри рамки.

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

Настройте другие шаблоны HTMLНастройте другие шаблоны HTML

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

  1. Создайте и откройте базовый файл-шаблон base.html в директории templates:

    nano templates/base.html
    
  2. Добавьте в файл следующий код:

    <!doctype html>
    <html lang="en">
      <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    
        <title>{% block title %} {% endblock %}</title>
      </head>
      <body>
        <nav class="navbar navbar-expand-lg bg-light">
          <div class="container-fluid">
            <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
              <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                  <a class="nav-link active" href="#">About</a>
                </li>
              </ul>
            </div>
          </div>
        </nav>
        <div class="container">
          {% block content %} {% endblock %}
        </div>
    
        <!-- Optional JavaScript -->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
      </body>
    </html>
    

    В этот файл входит HTML-код, а также дополнительный код, необходимый для Bootstrap. Теги <meta> содержат информацию для браузера, тег <link> — привязывает файлы CSS Bootstrap, а тег <script> — ссылается на сценарий JavaScript, который позволяет реализовать ряд дополнительных функций Bootstrap. Подробнее см. в документации Bootstrap.

    Следующие части кода относятся к механизму шаблонов Jinja:

    • {% block title %} {% endblock %} — блок, замещающий заголовок. Вы будете использовать его в других шаблонах для написания уникальных заголовков для разных страниц приложения. Это позволит избежать перезаписи раздела <head>.
    • {{ url_for('index')}} — вызов функции, которая возвращает URL для функции визуализации index(). Этот вызов отличается от предыдущего вызова url_for(), который использовался для привязки файла CSS. Сейчас в функцию передается только один аргумент — имя функции визуализации, которая вместо файла привязывается к маршруту, связанному с этой функцией визуализации.
    • {% block content %} {% endblock %} — блок, в который будет подставлен определенный контент в зависимости от конечного шаблона.

    Сохраните и закройте файл base.html.

  3. Настройте в HTML-шаблонах наследование из базового шаблона. Откройте шаблон index.html:

    nano templates/index.html
    
  4. Замените содержимое файла следующим кодом:

    {% extends 'base.html' %}
    
    {% block title %} Welcome to FlaskBlog {% endblock %}
    
    {% block content %}
      <h1> Welcome to FlaskBlog </h1>
    {% endblock %}
    

    В этом коде:

    • {% extends 'base.html' %} — указывает, что текущий шаблон наследует из шаблона base.html.
    • {% block title %} ... {% endblock %} — содержит заголовок, который будет подставлен вместо переменной в соответствующий блок шаблона-родителя base.html.
    • {% block content %} ... {% endblock %} — содержит контент, который будет подставлен вместо переменной в соответствующий блок шаблона-родителя base.html.

    Поскольку один и тот же текст Welcome to FlaskBlog используется и в качестве названия страницы, и в качестве заголовка, который появляется под панелью навигации, то этот код можно сократить, избежав повторения:

    {% extends 'base.html' %}
    
    {% block content %}
      <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% endblock %}
    

    Сохраните и закройте файл index.html.

  5. Обновите главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000/.

    Вы увидите страницу с панелью навигации и оформленным заголовком.

Настройте базу данныхНастройте базу данных

Создайте базу данных, в которой будут храниться посты блога, и добавьте в нее несколько примеров постов.

Для работы с данными веб-приложения вы будете использовать СУБД SQLite, поскольку необходимый для взаимодействия с БД модуль sqlite3 уже входит в состав Python.

Подготовьте файл schema.sql, содержащий набор SQL-команд для создания базы данных, состоящей из таблицы posts с несколькими столбцами.

  1. Создайте и откройте файл schema.sql в директории проекта flask_blog:

    nano schema.sql
    
  2. Добавьте в файл следующий код:

    DROP TABLE IF EXISTS posts;
    
    CREATE TABLE posts (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        title TEXT NOT NULL,
        content TEXT NOT NULL
    );
    

    В этом коде:

    • Команда DROP TABLE IF EXISTS posts; удаляет таблицу posts, если она уже существует. Этот шаг рекомендуется выполнять перед созданием любой новой таблицы, так как он позволяет исключить возможные ошибки.
    • Команда CREATE TABLE posts создает таблицу posts со следующими столбцами:
      • id — целое число, первичный ключ (PRIMARY KEY) таблицы. AUTOINCREMENT означает, что значением будет уникальное число, сгенерированное автоматически при вставке новой записи (поста блога) в таблицу.
      • created — время создания новой записи. NOT NULL означает, что значение в столбце не может быть пустым, а DEFAULT CURRENT_TIMESTAMP​​​ — что значением по умолчанию будет момент добавления новой записи в таблицу. Это позволит не указывать вручную время создания поста, оно будет подставляться автоматически.
      • title — заголовок поста.
      • content — основной текст поста.

    Сохраните и закройте файл schema.sql.

    Примечание

    При использовании этих SQL-команд любое предыдущее содержимое таблицы posts в файле database.db будет удалено. Не сохраняйте ничего важного в базу данных через веб-приложение до окончания изучения этого руководства.

  3. Создайте и откройте файл init_db.py в директории проекта flask_blog:

    nano init_db.py
    

    Этот файл позволит сформировать файл базы данных SQLite для вашего веб-приложения.

  4. Добавьте в файл следующий код:

    import sqlite3
    
    connection = sqlite3.connect('database.db')
    
    
    with open('schema.sql') as f:
        connection.executescript(f.read())
    
    cur = connection.cursor()
    
    cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
                ('First Post', 'Content for the first post')
                )
    
    cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
                ('Second Post', 'Content for the second post')
                )
    
    connection.commit()
    connection.close()
    

    В этом коде:

    • Импортируется модуль sqlite3.
    • Открывается соединение с файлом базы данных database.db, который будет создан сразу после запуска файла Python.
    • С помощью функции open() открывается созданная ранее SQL-схема schema.sql.
    • С помощью метода executescript() оформляется содержание схемы: выполняется одновременно несколько операторов SQL и формируется таблица posts.
    • С помощью метода cursor() открывается курсор, необходимый для отправки запросов к базе данных.
    • С помощью метода execute() выполняются два оператора SQL INSERT, которые добавляют два поста-примера в таблицу posts.
    • Изменения фиксируются, и соединение закрывается.

    Сохраните и закройте файл init_db.py.

  5. Запустите выполнение файла с помощью python:

    python init_db.py
    
  6. Убедитесь, что в директории проекта flask_blog появился файл database.db:

    ls -l
    

    Результат:

    total 40
    -rw-rw-r-- 1 yc-user yc-user   105 Mar 23 19:45 app.py
    -rw-r--r-- 1 yc-user yc-user 12288 Mar 24 12:27 database.db
    drwxrwxr-x 5 yc-user yc-user  4096 Mar 23 19:30 env
    -rw-rw-r-- 1 yc-user yc-user   472 Mar 24 12:27 init_db.py
    drwxrwxr-x 2 yc-user yc-user  4096 Mar 23 19:45 __pycache__
    -rw-rw-r-- 1 yc-user yc-user   204 Mar 24 12:26 schema.sql
    drwxrwxr-x 2 yc-user yc-user  4096 Mar 24 12:29 static
    drwxrwxr-x 2 yc-user yc-user  4096 Mar 24 12:30 templates
    

Настройте отображение постов блогаНастройте отображение постов блога

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

Отобразите список всех постов блогаОтобразите список всех постов блога

Измените функцию визуализации index() и шаблон index.html, чтобы отобразить список всех постов, сохраненных в базе данных.

  1. Откройте файл app.py:

    nano app.py
    
  2. Замените содержимое файла следующим кодом:

    import sqlite3
    from flask import Flask, render_template
    
    def get_db_connection():
        conn = sqlite3.connect('database.db')
        conn.row_factory = sqlite3.Row
        return conn
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        conn = get_db_connection()
        posts = conn.execute('SELECT * FROM posts').fetchall()
        conn.close()
        return render_template('index.html', posts=posts)
    

    В этом коде дополнительно:

    • Импортируется модуль sqlite3.
    • Создается функция get_db_connection(), в которой:
      • создается объект подключения conn к базе данных database.db;
      • данные БД присваиваются атрибуту row_factory объекта conn;
      • возвращается объект подключения conn, который будет использоваться для доступа к БД.
    • В измененной функции index():
      • объекту conn присваивается объект подключения к БД, возвращенный функцией get_db_connection();
      • с помощью метода fetchall() объекту posts присваивается результат выполнения SQL-запроса для выбора всех записей из таблицы posts;
      • с помощью метода close() закрывается подключение к базе данных;
      • возвращается HTML-код главной страницы приложения, сформированный из шаблона index.html, в который в качестве аргумента передается объект posts, содержащий все записи таблицы с постами.

    Сохраните и закройте файл app.py.

  3. Откройте шаблон главной страницы приложения index.html:

    nano templates/index.html
    
  4. Замените содержимое файла следующим кодом:

    {% extends 'base.html' %}
    
    {% block content %}
      <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
      {% for post in posts %}
        <a href="#">
          <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge text-bg-primary">{{ post['created'] }}</span>
        <hr>
      {% endfor %}
    {% endblock %}
    

    В этом коде добавляются:

    • {% for post in posts %} ... {% endfor %} — цикл Jinja, который отображает по порядку каждый элемент, содержащийся в объекте posts, переданном функцией index() в файле app.py. Внутри этого цикла для каждого поста в HTML-заголовке <h2>​​ отображается заголовок поста ​со ссылкой. В дальнейшем эти ссылки будут использоваться для перехода на страницы просмотра отдельных постов.
    • {{ post['title'] }} — значение заголовка поста, который содержится в переменной post, то есть в элементе объекта posts, обрабатываемом в текущей итерации цикла for.
    • Аналогично отображается дата создания поста post['created'], к дате применяется CSS-класс badge.

    Сохраните и закройте файл index.html.

  5. Обновите главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000/.

    Вы увидите список заголовков двух постов, которые вы добавили в базу данных при ее создании.

Отобразите отдельные посты блогаОтобразите отдельные посты блога

Создайте новый маршрут Flask с функцией визуализации, которая будет обрабатывать новый HTML-шаблон для отображения отдельного поста блога по его идентификатору. С помощью нового маршрута приложение по URL http://<публичный_IP-адрес_ВМ>:5000/ID будет отображать пост с соответствующим номером ID, если такой идентификатор существует в базе данных.

  1. Откройте файл app.py:

    nano app.py
    
  2. Замените содержимое файла следующим кодом:

    import sqlite3
    from flask import Flask, render_template
    from werkzeug.exceptions import abort
    
    def get_db_connection():
        conn = sqlite3.connect('database.db')
        conn.row_factory = sqlite3.Row
        return conn
    
    def get_post(post_id):
        conn = get_db_connection()
        post = conn.execute('SELECT * FROM posts WHERE id = ?',
                            (post_id,)).fetchone()
        conn.close()
        if post is None:
            abort(404)
        return post
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        conn = get_db_connection()
        posts = conn.execute('SELECT * FROM posts').fetchall()
        conn.close()
        return render_template('index.html', posts=posts)
    
    @app.route('/<int:post_id>')
    def post(post_id):
        post = get_post(post_id)
        return render_template('post.html', post=post)
    

    В этом коде дополнительно:

    • Импортируется функция abort() из библиотеки Werkzeug. Она позволит формировать ответ Flask с сообщением 404 Not Found​​​, если пост блога с указанным в URL идентификатором не существует.
    • Создается функция get_post(), принимающая аргумент post_id с идентификатором поста блога, который необходимо получить. В этой функции:
      • объекту conn присваивается объект подключения к БД, возвращенный функцией get_db_connection();
      • с помощью метода fetchone() объекту post присваивается результат выполнения SQL-запроса, получающего из БД строку с постом блога с идентификатором, соответствующим значению post_id;
      • с помощью метода close() закрывается подключение к базе данных;
      • если пост с переданным в post_id идентификатором не найден, используется функция abort() для ответа с кодом ошибки 404, после чего выполнение функции завершается;
      • если пост с переданным в post_id идентификатором найден, функция возвращает объект post, содержащий строку таблицы БД для этого поста.
    • Создается функция визуализации post(post_id). В этой функции:
      • декоратором добавляется правило переменной <int:post_id>, которое указывает, что URL после / представляет собой положительное целое число (отмеченное конвертером int), использующееся функцией визуализации;
      • с помощью функции get_post(post_id) объекту post присваивается содержимое строки таблицы БД, соответствующей посту блога с указанным идентификатором;
      • возвращается HTML-код страницы просмотра поста, сформированный из шаблона post.html, в который в качестве аргумента передается объект post с содержимым поста.

    Сохраните и закройте файл app.py.

  3. Создайте и откройте файл-шаблон post.html в директории templates:

    nano templates/post.html
    
  4. Добавьте в файл следующий код:

    {% extends 'base.html' %}
    
    {% block content %}
        <h2>{% block title %} {{ post['title'] }} {% endblock %}</h2>
        <p>{{ post['content'] }}</p>
        <span class="badge text-bg-primary">{{ post['created'] }}</span>
    {% endblock %}
    

    Этот код похож на код в шаблоне index.html. Различие в том, что здесь не используется цикл for, так как шаблон должен отображать единичный пост с его содержимым post['content'].

    Сохраните и закройте файл post.html.

  5. Откройте URL http://<публичный_IP-адрес_ВМ>:5000/1 и http://<публичный_IP-адрес_ВМ>:5000/2, чтобы просмотреть два первых поста, созданных одновременно с базой данных.

    Откройте URL http://<публичный_IP-адрес_ВМ>:5000/3, и вы увидите страницу с ошибкой о том, что запрашиваемый пост блога не найден.

  6. Добавьте на главную страницу приложения ссылки, ведущие с заголовков постов на страницы просмотра их содержимого.

    Откройте шаблон index.html:

    nano templates/index.html
    
  7. Замените значение атрибута href с # на {{ url_for('post', post_id=post['id']) }}, чтобы цикл for выглядел следующим образом:

    ...
      {% for post in posts %}
        <a href="{{ url_for('post', post_id=post['id']) }}">
          <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge text-bg-primary">{{ post['created'] }}</span>
        <hr>
      {% endfor %}
    ...
    

    Функция url_for() в качестве аргументов принимает:

    • 'post' — функцию визуализации post().
    • post_id — переменную со значением идентификатора поста post['id'], необходимого для функции визуализации.

    Функция url_for() для каждого заголовка поста возвращает корректный URL, основанный на его идентификаторе.

    Сохраните и закройте файл index.html.

  8. Перейдите к главной странице приложения http://<публичный_IP-адрес_ВМ>:5000/ и убедитесь, что заголовки постов работают как ссылки для перехода к их содержимому.

Добавьте действия с постамиДобавьте действия с постами

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

Создание нового постаСоздание нового поста

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

  1. Откройте файл app.py:

    nano app.py
    
  2. Замените содержимое файла следующим кодом:

    import sqlite3
    from flask import Flask, render_template, request, url_for, flash, redirect
    from werkzeug.exceptions import abort
    
    def get_db_connection():
        conn = sqlite3.connect('database.db')
        conn.row_factory = sqlite3.Row
        return conn
    
    def get_post(post_id):
        conn = get_db_connection()
        post = conn.execute('SELECT * FROM posts WHERE id = ?',
                            (post_id,)).fetchone()
        conn.close()
        if post is None:
            abort(404)
        return post
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'your secret key'
    
    @app.route('/')
    def index():
        conn = get_db_connection()
        posts = conn.execute('SELECT * FROM posts').fetchall()
        conn.close()
        return render_template('index.html', posts=posts)
    
    @app.route('/<int:post_id>')
    def post(post_id):
        post = get_post(post_id)
        return render_template('post.html', post=post)
    
    @app.route('/create', methods=('GET', 'POST'))
    def create():
        return render_template('create.html')
    

    В этом коде дополнительно:

    • Импортируются:
      • глобальный объект request, необходимый для доступа к данным, передающимся через HTML-форму;
      • функция url_for(), необходимая для формирования URL-адресов;
      • функция flash(), необходимая для отображения сообщения при обработке запроса;
      • функция redirect(), необходимая для перенаправления клиента на другую страницу.
    • Через объект app.config добавляется конфигурация секретного ключа SECRET_KEY, необходимая для работы функции flash(), чтобы хранить всплывающие сообщения в сеансе браузера клиента. Секретный ключ представляет собой длинную строку случайных символов. Он используется для создания защищенных сеансов и позволяет Flask запоминать информацию между запросами, например переходить от страницы создания нового поста к главной странице приложения. Пользователь может получить доступ к информации, хранящейся в сеансе, но не может изменить ее без секретного ключа. Поэтому никогда не передавайте никому доступ к вашему секретному ключу. Подробнее см. в документации Flask.
    • Функция визуализации create() возвращает сформированный из шаблона create.html HTML-код страницы создания поста блога. Декоратор создает маршрут /create, который принимает GET и POST-запросы. По умолчанию принимаются только GET-запросы. Для того чтобы маршрут также принимал POST-запросы, которые используются браузером при передаче данных форм, в аргументе methods передается кортеж с допустимыми типами запросов.

    Сохраните и закройте файл app.py.

  3. Создайте и откройте файл-шаблон create.html в директории templates:

    nano templates/create.html
    
  4. Добавьте в файл следующий код:

    {% extends 'base.html' %}
    
    {% block content %}
    <h1>{% block title %} Create a New Post {% endblock %}</h1>
    
    <form method="post">
      <div class="mb-3">
        <label for="title" class="col-sm-2 col-form-label">Title</label>
        <input type="text" name="title"
               placeholder="Post title" class="form-control"
               value="{{ request.form['title'] }}">
      </div>
    
      <div class="mb-3">
        <label for="content" class="col-sm-2 col-form-label">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control" rows="3">{{ request.form['content'] }}</textarea>
      </div>
      <div class="mb-3">
        <button type="submit" class="btn btn-primary">Submit</button>
      </div>
    </form>
    {% endblock %}
    

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

    Значение заголовка поста сохраняется в объекте {{ request.form['title'] }}, а значение содержания — в объекте {{ request.form['content'] }}. Это сделано для того, чтобы вводимые вами данные не потерялись, если что-то пойдет не по плану. Например, если вы напишете основной текст поста, но забудете указать заголовок, отобразится сообщение о том, что заголовок является обязательным для заполнения полем. При этом вы не потеряете уже введенный основной текст, так как он будет сохранен в глобальном объекте request, к которому у вас есть доступ в шаблонах.

    Сохраните и закройте файл create.html.

  5. Откройте URL http://<публичный_IP-адрес_ВМ>:5000/create и убедитесь, что отображается страница Create a New Post с полями для заголовка и содержания поста, а также кнопкой для отправки формы.

    Форма с этой страницы отправляет POST-запрос в функцию визуализации create(). Но в функции сейчас отсутствует код обработки POST-запроса, поэтому после заполнения и отправки формы введенные данные не будут сохранены в БД. Исправьте это, изменив функцию визуализации create().

  6. Откройте файл app.py:

    nano app.py
    
  7. Замените содержимое функции визуализации create() на следующий код:

    ...
    @app.route('/create', methods=('GET', 'POST'))
    def create():
        if request.method == 'POST':
            title = request.form['title']
            content = request.form['content']
    
            if not title:
                flash('Title is required!')
            else:
                conn = get_db_connection()
                conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                             (title, content))
                conn.commit()
                conn.close()
                return redirect(url_for('index'))
    
        return render_template('create.html')
    

    В коде этой функции:

    • Условие if request.method == 'POST' обеспечивает, чтобы код выполнялся только в отношении POST-запросов.
    • Из глобального объекта request.form, содержащего данные формы, извлекаются отправленные в полях формы заголовок и содержание нового поста.
    • Если в форме не задан заголовок поста, будет выполнено условие if not title, и пользователь увидит сообщение с информацией о необходимости заполнения поля заголовка.
    • Если заголовок указан:
      • с помощью функции get_db_connection() открывается подключение к БД, и в таблицу posts добавляется запись о новом посте, содержащая полученные через форму заголовок и текст поста;
      • изменения фиксируются, соединение закрывается;
      • с помощью функции redirect() пользователь перенаправляется на главную страницу приложения.

    Сохраните и закройте файл app.py.

  8. Откройте URL http://<публичный_IP-адрес_ВМ>:5000/create, введите заголовок и содержание поста и отправьте форму. После отправки формы вы будете перенаправлены на главную страницу приложения, где вы увидите список всех постов, включая новый.

  9. В шаблоне base.html добавьте отображение всплывающего сообщения и добавьте на панель навигации ссылку на форму создания нового поста. Откройте шаблон base.html:

    nano templates/base.html
    
  10. Перед ссылкой About​​ внутри тега <nav> Добавьте тег <li>:

    ...
        <nav class="navbar navbar-expand-lg bg-light">
          <div class="container-fluid">
            <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
              <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                  <a class="nav-link active" href="{{url_for('create')}}">New Post</a>
                </li>
                <li class="nav-item">
                  <a class="nav-link active" href="#">About</a>
                </li>
              </ul>
            </div>
          </div>
        </nav>
    ...
    
  11. В блоке <div class="container"> непосредственно перед блоком content добавьте цикл for, необходимый для отображения всплывающих сообщений под панелью навигации:

    ...
        <div class="container">
          {% for message in get_flashed_messages() %}
            <div class="alert alert-danger">{{ message }}</div>
          {% endfor %}
          {% block content %} {% endblock %}
        </div>
    ...
    

    Всплывающие сообщения доступны в специальной функции Flask get_flashed_messages().

    Сохраните и закройте файл base.html.

    На панели навигации появился управляющий элемент New Post, связанный с маршрутом /create.

  12. Перейдите на главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000 и убедитесь, что по ссылке New Post на панели навигации можно создать новый пост.

Редактирование существующего постаРедактирование существующего поста

Создайте страницу, на которой можно отредактировать существующий пост.

  1. Откройте файл app.py:

    nano app.py
    
  2. В конце файла добавьте функцию визуализации edit():

    ...
    @app.route('/<int:id>/edit', methods=('GET', 'POST'))
    def edit(id):
        post = get_post(id)
    
        if request.method == 'POST':
            title = request.form['title']
            content = request.form['content']
    
            if not title:
                flash('Title is required!')
            else:
                conn = get_db_connection()
                conn.execute('UPDATE posts SET title = ?, content = ?'
                            ' WHERE id = ?',
                            (title, content, id))
                conn.commit()
                conn.close()
                return redirect(url_for('index'))
    
        return render_template('edit.html', post=post)
    

    Процесс редактирования существующего поста похож на процесс создания нового, поэтому эта функция визуализации почти идентична функции визуализации create(). Пост, который необходимо отредактировать, задается в URL, а идентификатор поста передается в функцию edit() в аргументе id. Это же значение передается и в функцию get_post(), чтобы получить текущее содержимое поста из базы данных. Измененные данные поступят в приложение в POST-запросе, который обрабатывается внутри условия if request.method == 'POST'.

    Как и при создании нового поста, функция извлекает в отдельные переменные данные из объекта request.form, после чего выполняет проверку на заполнение пользователем заголовка поста. Если заголовок не заполнен, функция показывает всплывающее сообщение об обязательности заполнения этого поля. В остальных случаях функция открывает подключение к базе данных и обновляет поля в строке с нужным постом в таблице posts. Идентификатор поста в базе данных соответствует идентификатору ID, указанному в URL.

    В случае GET-запроса функция возвращает HTML-код, сформированный из шаблона edit.html с подставленными из объекта post значениями заголовка и текста поста.

    Сохраните и закройте файл app.py.

  3. Создайте и откройте файл-шаблон edit.html в директории templates:

    nano templates/edit.html
    
  4. Добавьте в файл следующий код:

    {% extends 'base.html' %}
    
    {% block content %}
    <h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>
    
    <form method="post">
      <div class="mb-3">
        <label for="title" class="col-sm-2 col-form-label">Title</label>
        <input type="text" name="title" placeholder="Post title"
               class="form-control"
               value="{{ request.form['title'] or post['title'] }}">
      </div>
    
      <div class="mb-3">
        <label for="content" class="col-sm-2 col-form-label">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control" rows="3">{{ request.form['content'] or post['content'] }}</textarea>
      </div>
      <div class="mb-3">
        <button type="submit" class="btn btn-primary">Submit</button>
      </div>
    </form>
    <hr>
    {% endblock %}
    

    Код аналогичен коду шаблона create.html за исключением выражений {{ request.form['title'] or post['title'] }} и {{ request.form['content'] or post['content'] }}. Эти выражения отображают данные, сохраненные в запросе, а если запроса не существует, — данные из переданной в шаблон переменной post с текущими значениями из базы данных.

    Сохраните и закройте файл edit.html.

  5. Откройте URL http://<публичный_IP-адрес_ВМ>:5000/1/edit для редактирования первого поста. Вы увидите страницу Edit "First Post" с данными поста. Отредактируйте пост, отправьте форму и убедитесь, что пост был обновлен.

  6. На главной странице приложения добавьте для каждого поста из списка ссылку на страницу редактирования. Откройте шаблон index.html:

    nano templates/index.html
    
  7. После даты создания поста добавьте ссылку для его редактирования:

    ...
      {% for post in posts %}
        <a href="{{ url_for('post', post_id=post['id']) }}">
          <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge text-bg-primary">{{ post['created'] }}</span>
        <a href="{{ url_for('edit', id=post['id']) }}">
          <span class="badge text-bg-warning">Edit</span>
        </a>
        <hr>
      {% endfor %}
    ...
    

    К заголовкам постов на главной странице добавляется ссылка Edit, которая указывает на функцию визуализации edit() и передает в нее значение идентификатора поста из post['id'].

    Сохраните и закройте файл index.html.

  8. Перейдите на главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000 и убедитесь, что на ней появились ссылки Edit, с помощью которых можно изменять содержимое постов.

Удаление постаУдаление поста

  1. Откройте файл app.py:

    nano app.py
    
  2. В конце файла добавьте функцию визуализации delete():

    ...
    @app.route('/<int:id>/delete', methods=('POST',))
    def delete(id):
        post = get_post(id)
        conn = get_db_connection()
        conn.execute('DELETE FROM posts WHERE id = ?', (id,))
        conn.commit()
        conn.close()
        flash('"{}" was successfully deleted!'.format(post['title']))
        return redirect(url_for('index'))
    

    Эта функция принимает только POST-запросы. Это значит, что просто переход в браузере по адресу http://<публичный_IP-адрес_ВМ>:5000/ID/delete вернет ошибку, поскольку веб-браузеры по умолчанию используют GET-запросы. POST-запрос по этому маршруту можно отправить только через форму, передающую идентификатор поста, который требуется удалить.

    Получив POST-запрос, функция открывает подключение к базе данных и выполняет SQL-команду DELETE FROM, после чего изменения фиксируются, а подключение закрывается. Приложение перенаправляет пользователя на главную страницу и показывает сообщение о том, что пост был успешно удален.

    Кнопка Delete будет добавлена на страницу редактирования поста, поэтому для этой функции не нужен отдельный шаблон.

    Сохраните и закройте файл app.py.

  3. Откройте шаблон edit.html:

    nano templates/edit.html
    
  4. Непосредственно после тега <hr> и перед строкой {% endblock %} добавьте тег <form>:

    ...
    <hr>
    <form action="{{ url_for('delete', id=post['id']) }}" method="POST">
      <input type="submit" value="Delete Post"
             class="btn btn-danger btn-sm"
             onclick="return confirm('Are you sure you want to delete this post?')">
    </form>
    {% endblock %}
    

    В этом коде используется метод confirm(), запрашивающий подтверждение согласия пользователя перед отправкой запроса.

    Сохраните и закройте файл edit.html.

  5. Перейдите на главную страницу приложения http://<публичный_IP-адрес_ВМ>:5000 и убедитесь, что на странице редактирования поста появилась кнопка Delete Post, удаляющая пост.

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

Подведите итогиПодведите итоги

В ходе выполнения данного руководства вы использовали фреймворк Flask, СУБД SQLite, набор инструментов Werkzeug, механизм шаблонов Jinja и инструментарий Bootstrap, чтобы создать небольшое веб-приложение. В результате вы получили полностью функциональный блог, позволяющий создавать, читать, редактировать и удалять посты при помощи параметров URL и HTML-форм.

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

  • Flask-Login — позволяет управлять сеансами пользователей: решает задачи входа в систему и выхода из системы, а также управления сеансами пользователей.
  • Flask-SQLAlchemy — упрощает использование Flask за счет поддержки SQLAlchemy. Это инструментарий Python SQL и Object Relational Mapper, разработанный для взаимодействия с базами данных.
  • Flask-Mail — помогает отправлять сообщения электронной почты из приложений Flask.

Удалите созданные ресурсыУдалите созданные ресурсы

Чтобы перестать платить за развернутый веб-сервер, удалите виртуальную машину.

Если вы зарезервировали для вашего веб-сервера статический публичный IP-адрес, удалите его.

Была ли статья полезна?

Предыдущая
Terraform
Следующая
Создание SAP-программы в Yandex Cloud
Проект Яндекса
© 2025 ООО «Яндекс.Облако»