Поиск по сайту:

Как сделать веб-приложение с помощью Flask в Python 3


Автор выбрал программу Write for DOnations.

Введение

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

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

Flask использует механизм шаблонов Jinja для динамического создания HTML-страниц с использованием знакомых концепций Python, таких как переменные, циклы, списки и т. д. Вы будете использовать эти шаблоны как часть этого проекта.

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

Предпосылки

Прежде чем вы начнете следовать этому руководству, вам потребуется:

  • Локальная среда программирования Python 3. Следуйте инструкциям для вашего дистрибутива в разделе Установка и настройка локальной среды программирования для серии Python 3 для вашего локального компьютера. В этом руководстве мы назовем каталог нашего проекта flask_blog.
  • Понимание концепций Python 3, таких как серия статей How To Code in Python 3.

Шаг 1 — Установка Flask

На этом шаге вы активируете среду Python и установите Flask с помощью установщика пакета pip.

Если вы еще не активировали среду программирования, убедитесь, что вы находитесь в каталоге проекта (flask_blog) и используйте следующую команду для активации среды:

  1. source env/bin/activate

Как только ваша среда программирования будет активирована, ваше приглашение будет иметь префикс env, который может выглядеть следующим образом:

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

Примечание. Вы можете использовать статью «Введение в установку Git, использование и ветки».

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

Теперь вы установите пакеты Python и изолируете код своего проекта от основной установки системы Python. Вы сделаете это, используя pip и python.

Чтобы установить Flask, выполните следующую команду:

  1. pip install flask

После завершения установки выполните следующую команду, чтобы подтвердить установку:

  1. python -c "import flask; print(flask.__version__)"

Вы используете интерфейс командной строки python с параметром -c для выполнения кода Python. Затем вы импортируете пакет flask с помощью import flask;, а затем печатаете версию Flask, которая предоставляется через переменную flask.__version__.

На выходе будет номер версии, подобный следующему:

Output
1.1.2

Вы создали папку проекта, виртуальную среду и установили Flask. Теперь вы готовы перейти к настройке базового приложения.

Шаг 2 — Создание базового приложения

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

В вашем каталоге flask_blog откройте файл с именем hello.py для редактирования, используйте nano или ваш любимый текстовый редактор:

  1. nano hello.py

Этот файл hello.py послужит минимальным примером того, как обрабатывать HTTP-запросы. Внутри него вы импортируете объект Flask и создаете функцию, которая возвращает HTTP-ответ. Напишите следующий код внутри hello.py:

from flask import Flask

app = Flask(__name__)


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

В предыдущем блоке кода вы сначала импортируете объект Flask из пакета flask. Затем вы используете его для создания своего экземпляра приложения Flask с именем app. Вы передаете специальную переменную __name__, которая содержит имя текущего модуля Python. Он используется, чтобы сообщить экземпляру, где он находится — вам это нужно, потому что Flask устанавливает некоторые пути за кулисами.

Создав экземпляр app, вы используете его для обработки входящих веб-запросов и отправки ответов пользователю. @app.route — это декоратор, который превращает обычную функцию Python в функцию просмотра Flask, которая преобразует возвращаемое значение функции в HTTP-ответ, отображаемый HTTP-клиентом. , например веб-браузер. Вы передаете значение / в @app.route(), чтобы указать, что эта функция будет отвечать на веб-запросы для URL-адреса /, который основной URL.

Функция просмотра hello() возвращает строку Hello, World! в качестве ответа.

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

Чтобы запустить ваше веб-приложение, вы сначала сообщите Flask, где найти приложение (файл hello.py в вашем случае) с переменной среды FLASK_APP:

  1. export FLASK_APP=hello

Затем запустите его в режиме разработки с переменной окружения FLASK_ENV:

  1. export FLASK_ENV=development

Наконец, запустите приложение с помощью команды flask run:

  1. flask run

После запуска приложения вывод будет примерно таким:

Output
* Serving Flask app "hello" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 813-894-335

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

  • Название запущенного вами приложения.
  • Среда, в которой запускается приложение.
  • Режим отладки: включен означает, что отладчик Flask запущен. Это полезно при разработке, так как дает нам подробные сообщения об ошибках, когда что-то идет не так, что упрощает устранение неполадок.
  • Приложение выполняется локально по URL-адресу http://127.0.0.1:5000/, 127.0.0.1 — это IP-адрес, представляющий localhost вашего компьютера. и :5000 — это номер порта.

Откройте браузер и введите URL-адрес http://127.0.0.1:5000/, в ответ вы получите строку Hello, World!, это подтверждает, что ваш приложение успешно запущено.

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

Теперь вы можете оставить сервер разработки запущенным в терминале и открыть другое окно терминала. Перейдите в папку проекта, где находится hello.py, активируйте виртуальную среду, установите переменные среды FLASK_ENV и FLASK_APP и перейдите к следующие шаги. (Эти команды перечислены ранее на этом шаге.)

Примечание. При открытии нового терминала важно не забыть активировать виртуальную среду и установить переменные среды FLASK_ENV и FLASK_APP.

Пока сервер разработки приложения Flask уже запущен, невозможно запустить другое приложение Flask с помощью той же команды flask run. Это связано с тем, что flask run использует номер порта 5000 по умолчанию, и после того, как он занят, он становится недоступным для запуска другого приложения, поэтому вы получите сообщение об ошибке, аналогичное следующий:

Output
OSError: [Errno 98] Address already in use

Чтобы решить эту проблему, либо остановите работающий в данный момент сервер с помощью CTRL+C, затем снова запустите flask run, либо, если вы хотите запустить оба одновременно, можно передать другой номер порта в аргумент -p, например, чтобы запустить другое приложение на порту 5001, используйте следующую команду:

  1. flask run -p 5001

Теперь у вас есть небольшое веб-приложение Flask. Вы запустили приложение и отобразили информацию в веб-браузере. Далее вы будете использовать файлы HTML в своем приложении.

Шаг 3 — Использование HTML-шаблонов

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

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

На этом этапе вы создадите основное приложение Flask в новом файле.

Сначала в каталоге flask_blog используйте nano или ваш любимый редактор, чтобы создать и отредактировать файл app.py. Здесь будет храниться весь код, который вы будете использовать для создания приложения для ведения блога:

  1. nano app.py

В этом новом файле вы импортируете объект Flask для создания экземпляра приложения Flask, как вы это делали ранее. Вы также импортируете вспомогательную функцию render_template(), которая позволяет отображать файлы шаблонов HTML, которые существуют в папке templates, которую вы собираетесь создать. Файл будет иметь единственную функцию просмотра, которая будет отвечать за обработку запросов к основному маршруту /. Добавьте следующее содержимое:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

Функция просмотра index() возвращает результат вызова render_template() с index.html в качестве аргумента, это сообщает render_template( ), чтобы найти файл с именем index.html в папке шаблонов. И папка, и файл еще не существуют, вы получите сообщение об ошибке, если запустите приложение в этот момент. Тем не менее, вы запустите его, так что вы знакомы с этим часто встречающимся исключением. Затем вы исправите это, создав нужную папку и файл.

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

Остановите сервер разработки на другом терминале, на котором запущено приложение hello, с помощью CTRL+C.

Прежде чем запускать приложение, убедитесь, что вы правильно указали значение переменной среды FLASK_APP, поскольку вы больше не используете приложение hello:

  1. export FLASK_APP=app
  2. flask run

Открытие URL-адреса http://127.0.0.1:5000/ в вашем браузере приведет к тому, что страница отладчика сообщит вам, что шаблон index.html не найден. Основная строка кода, отвечающая за эту ошибку, будет выделена. В данном случае это строка return render_template(index.html).

Если щелкнуть эту строку, отладчик покажет больше кода, так что у вас будет больше контекста, который поможет вам решить проблему.

Чтобы исправить эту ошибку, создайте каталог с именем templates внутри вашего каталога flask_blog. Затем внутри него откройте файл с именем index.html для редактирования:

  1. mkdir templates
  2. nano templates/index.html

Затем добавьте следующий код HTML внутрь index.html:

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

Сохраните файл и с помощью браузера снова перейдите на http://127.0.0.1:5000/ или обновите страницу. На этот раз браузер должен отобразить текст Welcome to FlaskBlog в теге <h1>.

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

Вы можете создать файл таблицы стилей style.css, чтобы добавить CSS в свое приложение. Сначала создайте каталог с именем static внутри основного каталога flask_blog:

  1. mkdir static

Затем создайте другой каталог с именем css внутри каталога static для размещения файлов .css. Обычно это делается для организации статических файлов в выделенных папках, поэтому файлы JavaScript обычно находятся в каталоге с именем js, изображения помещаются в каталог с именем images (или img) и так далее. Следующая команда создаст каталог css внутри каталога static:

  1. mkdir static/css

Затем откройте файл style.css в каталоге css для редактирования:

  1. nano static/css/style.css

Добавьте следующее правило CSS в файл style.css:

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

Код CSS добавит рамку, изменит цвет на коричневый, отцентрирует текст и добавит небольшой отступ к тегам <h1>.

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

Затем откройте файл шаблона index.html для редактирования:

  1. nano templates/index.html

Вы добавите ссылку на файл style.css в раздел <head> файла шаблона index.html:

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

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

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

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

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

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

Чтобы создать базовый шаблон, сначала создайте файл с именем base.html в каталоге templates:

  1. nano templates/base.html

Введите следующий код в свой шаблон base.html:

<!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 rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>{% block title %} {% endblock %}</title>
  </head>
  <body>
    <nav class="navbar navbar-expand-md navbar-light bg-light">
        <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav">
            <li class="nav-item active">
                <a class="nav-link" href="#">About</a>
            </li>
            </ul>
        </div>
    </nav>
    <div class="container">
        {% block content %} {% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" 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), которые переопределяют его.

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

  1. nano templates/index.html

Затем замените его содержимое следующим:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
{% endblock %}

В этой новой версии шаблона index.html вы используете тег {% extends %} для наследования от шаблона base.html. Затем вы расширяете его, заменяя блок content в базовом шаблоне тем, что находится внутри блока content в предыдущем блоке кода.

Этот блок content содержит тег <h1> с текстом Welcome to FlaskBlog внутри блока title, который в Turn заменяет исходный блок title в шаблоне base.html текстом Welcome to FlaskBlog. Таким образом, вы можете избежать повторения одного и того же текста дважды, так как он работает и как заголовок страницы, и как заголовок, который появляется под панелью навигации, унаследованной от базового шаблона.

Наследование шаблонов также дает вам возможность повторно использовать HTML-код, который у вас есть в других шаблонах (в данном случае base.html), без необходимости повторять его каждый раз, когда это необходимо.

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

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

Шаг 4 — Настройка базы данных

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

Вы будете использовать этот учебник.

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

Откройте файл с именем schema.sql в вашем каталоге flask_blog:

  1. nano schema.sql

Введите в этот файл следующие команды SQL:

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
);

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

Первая команда SQL — DROP TABLE IF EXISTS posts;, она удаляет все уже существующие таблицы с именами posts, чтобы избежать путаницы. Обратите внимание, что это удалит весь контент, который у вас есть в базе данных, всякий раз, когда вы используете эти команды SQL, поэтому убедитесь, что вы не записываете какой-либо важный контент в веб-приложение, пока не закончите это руководство и не поэкспериментируете с окончательным результатом. Затем CREATE TABLE posts используется для создания таблицы posts со следующими столбцами:

  • id: целое число, представляющее первичный ключ. База данных присваивает ему уникальное значение для каждой записи (т. е. сообщения в блоге).
  • создано: время создания сообщения в блоге. NOT NULL означает, что этот столбец не должен быть пустым, а значение DEFAULT — это значение CURRENT_TIMESTAMP, то есть время, когда сообщение было добавлено. в базу данных. Как и в случае с id, вам не нужно указывать значение для этого столбца, так как оно будет заполнено автоматически.
  • title: заголовок сообщения.
  • content: содержание публикации.

Теперь, когда у вас есть схема SQL в файле schema.sql, вы будете использовать ее для создания базы данных с помощью файла Python, который сгенерирует файл базы данных SQLite .db. . Откройте файл с именем init_db.py в каталоге flask_blog с помощью предпочитаемого вами редактора:

  1. nano init_db.py

А затем добавьте следующий код.

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(), чтобы открыть файл schema.sql. Затем вы выполняете его содержимое, используя метод execute(), чтобы выполнить две инструкции SQL INSERT, чтобы добавить две записи блога в вашу таблицу posts. Наконец, вы фиксируете изменения и закрываете соединение.

Сохраните и закройте файл, а затем запустите его в терминале с помощью команды python:

  1. python init_db.py

После завершения выполнения файла в вашем каталоге flask_blog появится новый файл с именем database.db. Это означает, что вы успешно настроили свою базу данных.

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

Шаг 5 — Отображение всех сообщений

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

Откройте файл app.py, чтобы внести следующие изменения:

  1. nano app.py

Для вашей первой модификации вы импортируете модуль sqlite3 вверху файла:

import sqlite3
from flask import Flask, render_template

. . .

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

. . .
from flask import Flask, render_template

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn

. . .

Эта функция get_db_connection() открывает соединение с файлом базы данных database.db, а затем устанавливает для атрибута row_factory значение sqlite3.Row. , чтобы вы могли иметь доступ к столбцам на основе имени. Это означает, что соединение с базой данных будет возвращать строки, которые ведут себя как обычные словари Python. Наконец, функция возвращает объект подключения conn, который вы будете использовать для доступа к базе данных.

После определения функции get_db_connection() измените функцию index(), чтобы она выглядела следующим образом:

. . .

@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

В этой новой версии функции index() вы сначала открываете соединение с базой данных, используя функцию get_db_connection(), которую вы определили ранее. Затем вы выполняете запрос SQL, чтобы выбрать все записи из таблицы posts. Вы реализуете метод fetchall() для получения всех строк результата запроса, это вернет список сообщений, которые вы вставили в базу данных на предыдущем шаге.

Вы закрываете соединение с базой данных с помощью метода close() и возвращаете результат рендеринга шаблона index.html. Вы также передаете объект posts в качестве аргумента, который содержит результаты, полученные вами из базы данных, это позволит вам получить доступ к сообщениям блога в шаблоне index.html.

После внесения этих изменений сохраните и закройте файл app.py.

Теперь, когда вы передали сообщения, полученные из базы данных, в шаблон index.html, вы можете использовать цикл for для отображения каждого сообщения на вашей индексной странице.

Откройте файл index.html:

  1. nano templates/index.html

Затем измените его, чтобы он выглядел следующим образом:

{% 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 badge-primary">{{ post['created'] }}</span>
        <hr>
    {% endfor %}
{% endblock %}

Здесь синтаксис {% for post in posts %} — это цикл Jinja for, который похож на цикл Python for, за исключением того, что он позже необходимо закрыть с помощью синтаксиса {% endfor %}. Вы используете этот синтаксис для циклического просмотра каждого элемента в списке posts, который был передан функцией index() в строке return render_template(index.html, posts =сообщения). Внутри этого цикла for вы отображаете заголовок сообщения в заголовке <h2> внутри тега <a> (позже вы будете использовать этот тег для ссылки на каждый пост отдельно).

Вы отображаете заголовок, используя литеральный разделитель переменной ({{ ... }}). Помните, что post будет объектом, подобным словарю, поэтому вы можете получить доступ к заголовку сообщения с помощью post[title]. Вы также отображаете дату создания сообщения, используя тот же метод.

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

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

Шаг 6 — Отображение отдельного сообщения

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

К концу этого шага URL-адрес http://127.0.0.1:5000/1 будет страницей, отображающей первое сообщение (поскольку он имеет идентификатор 1). ). URL-адрес http://127.0.0.1:5000/ID будет отображать сообщение с соответствующим номером ID. если он существует.

Откройте app.py для редактирования:

  1. nano app.py

Поскольку позже в этом проекте вам потребуется получить сообщение в блоге по его идентификатору из базы данных в нескольких местах, вы создадите автономную функцию с именем get_post(). Вы можете вызвать его, передав ему идентификатор и получить сообщение в блоге, связанное с предоставленным идентификатором, или заставить Flask ответить сообщением 404 Not Found, если сообщение в блоге не существует.

Чтобы ответить страницей 404, вам нужно импортировать библиотеку Werkzeug, которая была установлена вместе с Flask, вверху файла:

import sqlite3
from flask import Flask, render_template
from werkzeug.exceptions import abort

. . .

Затем добавьте функцию get_post() сразу после функции get_db_connection(), которую вы создали на предыдущем шаге:

. . .

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

. . .

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

Внутри функции вы используете функцию get_db_connection(), чтобы открыть соединение с базой данных и выполнить SQL-запрос, чтобы получить сообщение в блоге, связанное с заданным значением post_id. Вы добавляете метод fetchone(), чтобы получить результат и сохранить его в переменной post, а затем закрыть соединение. Если переменная post имеет значение None, означающее, что в базе данных не было найдено результата, вы используете импортированную ранее функцию abort() для ответьте кодом ошибки 404, и функция завершит выполнение. Однако если сообщение было найдено, вы возвращаете значение переменной post.

Затем добавьте следующую функцию просмотра в конец файла app.py:

. . .

@app.route('/<int:post_id>')
def post(post_id):
    post = get_post(post_id)
    return render_template('post.html', post=post)

В этой новой функции представления вы добавляете правило переменной , чтобы указать, что часть после косой черты (/) является положительное целое число (помеченное конвертером int), к которому вам нужно получить доступ в вашей функции представления. Flask распознает это и передает его значение аргументу ключевого слова post_id вашей функции представления post(). Затем вы используете функцию get_post(), чтобы получить сообщение в блоге, связанное с указанным идентификатором, и сохранить результат в переменной post, которую вы передаете в сообщение .html, который вы скоро создадите.

Сохраните файл app.py и откройте новый файл шаблона post.html для редактирования:

  1. nano templates/post.html

Введите следующий код в этот новый файл post.html. Это будет похоже на файл index.html, за исключением того, что он будет отображать только одно сообщение, в дополнение к отображению содержимого сообщения:

{% extends 'base.html' %}

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

Вы добавляете блок title, определенный в шаблоне base.html, чтобы заголовок страницы отражал заголовок сообщения, отображаемый в <h2>. заголовок одновременно.

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

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

http://127.0.0.1:5000/1
http://127.0.0.1:5000/2
http://127.0.0.1:5000/3

Вернувшись на индексную страницу, вы сделаете заголовок каждого поста ссылкой на соответствующую страницу. Вы сделаете это с помощью функции url_for(). Сначала откройте шаблон index.html для редактирования:

  1. nano templates/index.html

Затем измените значение атрибута 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 badge-primary">{{ post['created'] }}</span>
    <hr>
{% endfor %}

Здесь вы передаете post функции url_for() в качестве первого аргумента. Это имя функции представления post(), и поскольку она принимает аргумент post_id, вы присваиваете ей значение post[id]. Функция url_for() будет возвращать правильный URL-адрес для каждого сообщения на основе его идентификатора.

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

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

Шаг 7 — Изменение сообщений

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

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

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

Откройте файл app.py для редактирования:

  1. nano app.py

Во-первых, вы импортируете следующее из среды Flask:

  • Глобальный объект request для доступа к данным входящего запроса, которые будут отправлены через HTML-форму.
  • Функция url_for() для создания URL-адресов.
  • Функция flash() для отображения сообщения при обработке запроса.
  • Функция redirect() для перенаправления клиента в другое место.

Добавьте импорт в свой файл следующим образом:

import sqlite3
from flask import Flask, render_template, request, url_for, flash, redirect
from werkzeug.exceptions import abort

. . .

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

Чтобы установить секретный ключ, вы добавите конфигурацию SECRET_KEY в свое приложение через объект app.config. Добавьте его непосредственно после определения app перед определением функции представления index():

. . .
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('/create', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

Это создает маршрут /create, который принимает запросы GET и POST. GET-запросы принимаются по умолчанию. Чтобы также принимать POST-запросы, которые отправляются браузером при отправке форм, вы должны передать кортеж с принятыми типами запросов в аргумент methods функции @app.route(). декоратор.

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

Чтобы создать шаблон, откройте файл с именем create.html в папке templates:

  1. nano templates/create.html

Добавьте следующий код в этот новый файл:

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Create a New Post {% endblock %}</h1>

<form method="post">
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title"
               placeholder="Post title" class="form-control"
               value="{{ request.form['title'] }}"></input>
    </div>

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
{% endblock %}

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

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

Теперь, когда сервер разработки запущен, используйте браузер для перехода к маршруту /create:

http://127.0.0.1:5000/create

Вы увидите страницу «Создать новую публикацию» с полем для заголовка и содержания.

Эта форма отправляет запрос POST вашей функции просмотра create(). Однако в функции пока нет кода для обработки POST-запроса, поэтому после заполнения формы и ее отправки ничего не происходит.

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

Откройте файл app.py для редактирования:

  1. nano app.py

Измените функцию представления 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 вы гарантируете, что код, следующий за ним, выполняется только тогда, когда запрос является запросом POST посредством сравнения request.method == POST.

Затем вы извлекаете отправленный заголовок и содержимое из объекта request.form, который дает вам доступ к данным формы в запросе. Если заголовок не указан, условие if not title будет выполнено, отображая сообщение для пользователя, информирующее их о том, что заголовок требуется. Если, с другой стороны, указан заголовок, вы открываете соединение с помощью функции get_db_connection() и вставляете заголовок и полученный контент в таблицу posts.

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

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

Теперь перейдите к маршруту /create с помощью веб-браузера:

http://127.0.0.1:5000/create

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

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

  1. nano templates/base.html

Отредактируйте файл, добавив новый тег <li> после ссылки О программе внутри тега <nav>. Затем добавьте новый цикл for непосредственно над блоком content, чтобы отображать мигающие сообщения под панелью навигации. Эти сообщения доступны в специальной функции get_flashed_messages(), которую Flask предоставляет:

<nav class="navbar navbar-expand-md navbar-light bg-light">
    <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
        <li class="nav-item">
            <a class="nav-link" href="#">About</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{url_for('create')}}">New Post</a>
        </li>
        </ul>
    </div>
</nav>
<div class="container">
    {% for message in get_flashed_messages() %}
        <div class="alert alert-danger">{{ message }}</div>
    {% endfor %}
    {% block content %} {% endblock %}
</div>

Сохраните и закройте файл. На панели навигации теперь будет элемент Новое сообщение, связанный с маршрутом /create.

Редактирование сообщения

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

Сначала вы добавите новый маршрут в файл app.py. Его функция просмотра получит идентификатор поста, который нужно отредактировать, URL-адрес будет в формате /post_id/edit с post_id — идентификатор сообщения. Откройте файл app.py для редактирования:

  1. nano app.py

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

. . .

@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)

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

Как и при создании нового сообщения, вы сначала извлекаете данные из объекта request.form, а затем выводите сообщение, если заголовок имеет пустое значение, в противном случае вы открываете соединение с базой данных. Затем вы обновляете таблицу posts, устанавливая новый заголовок и новый контент, где идентификатор сообщения в базе данных равен идентификатору, который был в URL-адресе.

В случае запроса GET вы визуализируете шаблон edit.html, передавая переменную post, которая содержит возвращаемое значение get_post() функция. Вы будете использовать это для отображения существующего заголовка и контента на странице редактирования.

Сохраните и закройте файл, затем создайте новый шаблон edit.html:

  1. nano templates/edit.html

Напишите следующий код внутри этого нового файла:

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>

<form method="post">
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title" placeholder="Post title"
               class="form-control"
               value="{{ request.form['title'] or post['title'] }}">
        </input>
    </div>

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
<hr>
{% endblock %}

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

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

Теперь перейдите по следующему URL-адресу, чтобы отредактировать первое сообщение:

http://127.0.0.1:5000/1/edit

Вы увидите страницу редактирования \First Post.

Отредактируйте сообщение и отправьте форму, затем убедитесь, что сообщение было обновлено.

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

  1. nano templates/index.html

Отредактируйте файл, чтобы он выглядел точно так же, как показано ниже:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% for post in posts %}
        <a href="{{ url_for('post', post_id=post['id']) }}">
            <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge badge-primary">{{ post['created'] }}</span>
        <a href="{{ url_for('edit', id=post['id']) }}">
            <span class="badge badge-warning">Edit</span>
        </a>
        <hr>
    {% endfor %}
{% endblock %}

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

Здесь вы добавляете тег <a> для ссылки на функцию просмотра edit(), передавая значение post[id] для ссылки на страницу редактирования каждого сообщения со ссылкой Edit.

Удаление сообщения

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

Во-первых, вы добавите новый маршрут /ID/delete, который принимает запросы POST, подобно функции просмотра edit(). Ваша новая функция просмотра delete() получит идентификатор сообщения, которое нужно удалить, из URL-адреса. Откройте файл app.py:

  1. nano app.py

Добавьте следующую функцию просмотра внизу файла:

# ....

@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. Это означает, что при переходе по маршруту /ID/delete в вашем браузере будет возвращена ошибка, поскольку веб-браузеры по умолчанию используют запросы GET.

Однако вы можете получить доступ к этому маршруту через форму, которая отправляет запрос POST, передавая идентификатор сообщения, которое вы хотите удалить. Функция получит значение идентификатора и использует его для получения сообщения из базы данных с помощью функции get_post().

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

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

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

  1. nano templates/edit.html

Затем добавьте следующий тег <form> после тега <hr> и непосредственно перед строкой {% endblock %}:

<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() для отображения подтверждающего сообщения перед отправкой запроса.

Теперь снова перейдите на страницу редактирования сообщения в блоге и попробуйте удалить его:

http://127.0.0.1:5000/1/edit

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

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

Заключение

В этом руководстве представлены основные понятия инфраструктуры Flask Python. Вы узнали, как создать небольшое веб-приложение, запустить его на сервере разработки и разрешить пользователю предоставлять пользовательские данные с помощью параметров URL и веб-форм. Вы также использовали статью «Как использовать отношения базы данных «один ко многим» с Flask и SQLite».

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

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

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