2 added
2 removed
Original
2026-01-01
Modified
2026-02-21
1
<p>Язык Python - это лёгкая дорога в программирование. А Flask помогает проложить путь в веб-разработку и научиться писать сайты с помощью Python. Получается, он такой же лёгкий, как и Python? Да, но есть нюансы.</p>
1
<p>Язык Python - это лёгкая дорога в программирование. А Flask помогает проложить путь в веб-разработку и научиться писать сайты с помощью Python. Получается, он такой же лёгкий, как и Python? Да, но есть нюансы.</p>
2
<p>В этой статье мы расскажем, что собой представляет фреймворк Flask, чем он лучше других и когда стоит выбрать именно его. А параллельно напишем свой первый небольшой сайт, где даже сможем публиковать посты.</p>
2
<p>В этой статье мы расскажем, что собой представляет фреймворк Flask, чем он лучше других и когда стоит выбрать именно его. А параллельно напишем свой первый небольшой сайт, где даже сможем публиковать посты.</p>
3
<p>Всё, что нужно знать о Flask в Python:</p>
3
<p>Всё, что нужно знать о Flask в Python:</p>
4
<ul><li><a>Что это такое</a></li>
4
<ul><li><a>Что это такое</a></li>
5
<li><a>Чем он лучше других фреймворков</a></li>
5
<li><a>Чем он лучше других фреймворков</a></li>
6
<li><a>Как установить Flask</a></li>
6
<li><a>Как установить Flask</a></li>
7
<li><a>Как написать простой сайт</a></li>
7
<li><a>Как написать простой сайт</a></li>
8
<li><a>Как создать блог</a></li>
8
<li><a>Как создать блог</a></li>
9
<li><a>Что нужно запомнить</a></li>
9
<li><a>Что нужно запомнить</a></li>
10
</ul><p>Flask - это легковесный веб-фреймворк для языка Python, который предоставляет минимальный набор инструментов для создания веб-приложений. На нём можно сделать и лендинг, и многостраничный сайт с кучей плагинов и сервисов. Не фреймворк, а мечта!</p>
10
</ul><p>Flask - это легковесный веб-фреймворк для языка Python, который предоставляет минимальный набор инструментов для создания веб-приложений. На нём можно сделать и лендинг, и многостраничный сайт с кучей плагинов и сервисов. Не фреймворк, а мечта!</p>
11
<p>У Flask много преимуществ, которые выделяют его среди других фреймворков:</p>
11
<p>У Flask много преимуществ, которые выделяют его среди других фреймворков:</p>
12
<ul><li>простой синтаксис - это всё-таки Python;</li>
12
<ul><li>простой синтаксис - это всё-таки Python;</li>
13
<li>удобные шаблоны - можно быстро создавать прототипы веб-приложений;</li>
13
<li>удобные шаблоны - можно быстро создавать прототипы веб-приложений;</li>
14
<li>куча инструментов для гибкой настройки сайтов под любые нужды.</li>
14
<li>куча инструментов для гибкой настройки сайтов под любые нужды.</li>
15
-
</ul>Логотип Flask<em>Изображение: Wikimedia Commons</em><p>Ещё под Flask написаны сотни расширений и плагинов, которые добавляют дополнительные возможности - разные виды аутентификации, управление базами данных и работу с формами. И всё это - бесплатно и с открытым кодом.</p>
15
+
</ul>Логотип Flask<em>Изображение: Wikimedia Commons</em><p>Ещё под Flask написаны сотни расширений и плагинов, которые добавляют дополнительные воз��ожности - разные виды аутентификации, управление базами данных и работу с формами. И всё это - бесплатно и с открытым кодом.</p>
16
<p>Скачать фреймворк можно на <a>официальном сайте</a>. А если появятся вопросы по установке или настройке, то на помощь всегда придёт огромное сообщество Flask-разработчиков.</p>
16
<p>Скачать фреймворк можно на <a>официальном сайте</a>. А если появятся вопросы по установке или настройке, то на помощь всегда придёт огромное сообщество Flask-разработчиков.</p>
17
<p>Flask входит в топ-15 самых популярных фреймворков для веб-разработки среди опытных программистов. Рядом с ним в рейтинге находятся Django, Express.js, Laravel, Ruby on Rails, Spring и ASP.NET. И, конечно, на Flask написано немало популярных сайтов. Вот лишь несколько примеров:</p>
17
<p>Flask входит в топ-15 самых популярных фреймворков для веб-разработки среди опытных программистов. Рядом с ним в рейтинге находятся Django, Express.js, Laravel, Ruby on Rails, Spring и ASP.NET. И, конечно, на Flask написано немало популярных сайтов. Вот лишь несколько примеров:</p>
18
<ul><li><a>Pinterest</a> - одна из крупнейших социальных сетей для обмена изображениями и идеями;</li>
18
<ul><li><a>Pinterest</a> - одна из крупнейших социальных сетей для обмена изображениями и идеями;</li>
19
<li><a>Netflix</a> - один из крупнейших сервисов видеостриминга в мире;</li>
19
<li><a>Netflix</a> - один из крупнейших сервисов видеостриминга в мире;</li>
20
<li><a>Uber</a> - сервис вызова такси и автомобильного транспорта;</li>
20
<li><a>Uber</a> - сервис вызова такси и автомобильного транспорта;</li>
21
<li><a>Reddit</a> - один из самых популярных новостных UGC-агрегаторов;</li>
21
<li><a>Reddit</a> - один из самых популярных новостных UGC-агрегаторов;</li>
22
<li><a>Twilio</a> - платформа для разработки приложений для обмена сообщениями.</li>
22
<li><a>Twilio</a> - платформа для разработки приложений для обмена сообщениями.</li>
23
</ul><p>В общем, его используют везде: и в малом, и в крупном бизнесе, и на частных предприятиях, и в госучреждениях.</p>
23
</ul><p>В общем, его используют везде: и в малом, и в крупном бизнесе, и на частных предприятиях, и в госучреждениях.</p>
24
<p>У Flask есть ряд особенностей, за которые его любят веб-разработчики. Давайте их перечислим:</p>
24
<p>У Flask есть ряд особенностей, за которые его любят веб-разработчики. Давайте их перечислим:</p>
25
<ul><li><strong>Минимальный набор инструментов из коробки.</strong>Причём они не навязывают какую-то архитектуру или жёсткую структуру проектов. Разработчики сами решают, как и что они будут создавать.</li>
25
<ul><li><strong>Минимальный набор инструментов из коробки.</strong>Причём они не навязывают какую-то архитектуру или жёсткую структуру проектов. Разработчики сами решают, как и что они будут создавать.</li>
26
<li><strong>Гибкость.</strong>Работая с Flask, программист может выбирать только необходимые встроенные инструменты и подключать дополнительные внешние, не перегружая проект лишними модулями.</li>
26
<li><strong>Гибкость.</strong>Работая с Flask, программист может выбирать только необходимые встроенные инструменты и подключать дополнительные внешние, не перегружая проект лишними модулями.</li>
27
<li><strong>Расширяемость.</strong>У Flask много расширений и плагинов, которые помогают быстро добавить новую функциональность. Например, авторизацию, управление базами данных и работу с формами.</li>
27
<li><strong>Расширяемость.</strong>У Flask много расширений и плагинов, которые помогают быстро добавить новую функциональность. Например, авторизацию, управление базами данных и работу с формами.</li>
28
<li><strong>Простота.</strong>У Flask простой синтаксис, что делает изучение этого фреймворка более простым, а также позволяет быстрее создавать прототипы веб-приложений.</li>
28
<li><strong>Простота.</strong>У Flask простой синтаксис, что делает изучение этого фреймворка более простым, а также позволяет быстрее создавать прототипы веб-приложений.</li>
29
<li><strong>Поддержка сообщества.</strong>Flask запустили в 2010 году, и почти по любому связанному с ним вопросу в интернете уже есть ответы.</li>
29
<li><strong>Поддержка сообщества.</strong>Flask запустили в 2010 году, и почти по любому связанному с ним вопросу в интернете уже есть ответы.</li>
30
</ul><p>В общем, Flask как будто бы создан для новичков. Он несложен, в нём есть все необходимые базовые функции и возможности для расширения. Но при этом Flask может показаться слабеньким фреймворком, непригодным для крупных проектов. Кстати, это тоже можно исправить сторонними плагинами и библиотеками.</p>
30
</ul><p>В общем, Flask как будто бы создан для новичков. Он несложен, в нём есть все необходимые базовые функции и возможности для расширения. Но при этом Flask может показаться слабеньким фреймворком, непригодным для крупных проектов. Кстати, это тоже можно исправить сторонними плагинами и библиотеками.</p>
31
<p>Чтобы освоить азы Flask, в этой статье мы создадим небольшой сайт-блог и объясним главные концепции фреймворка в деле. Начнём с установки.</p>
31
<p>Чтобы освоить азы Flask, в этой статье мы создадим небольшой сайт-блог и объясним главные концепции фреймворка в деле. Начнём с установки.</p>
32
<p>Чтобы установить Flask, сначала нужно установить Python. Приступим.</p>
32
<p>Чтобы установить Flask, сначала нужно установить Python. Приступим.</p>
33
<p>Flask требует наличия Python версии 3.5 или выше. Если у вас нет Python, его можно загрузить с <a>официального сайта Python</a>. Подробную инструкцию можно посмотреть в нашем гайде по <a>установке Python для всех операционных систем</a>.</p>
33
<p>Flask требует наличия Python версии 3.5 или выше. Если у вас нет Python, его можно загрузить с <a>официального сайта Python</a>. Подробную инструкцию можно посмотреть в нашем гайде по <a>установке Python для всех операционных систем</a>.</p>
34
<p>Если вы скачиваете официальную версию Python или пакет Anaconda, у вас автоматически установится PIP. Это менеджер пакетов для Python, который позволяет управлять сторонними библиотеками. Нам он понадобится, чтобы установить Flask.</p>
34
<p>Если вы скачиваете официальную версию Python или пакет Anaconda, у вас автоматически установится PIP. Это менеджер пакетов для Python, который позволяет управлять сторонними библиотеками. Нам он понадобится, чтобы установить Flask.</p>
35
<p>Чтобы проверить, есть ли у вас PIP, введите в консоли:</p>
35
<p>Чтобы проверить, есть ли у вас PIP, введите в консоли:</p>
36
pip --version<p>или</p>
36
pip --version<p>или</p>
37
python3 -m pip --version<p>В ответ на экран выведется версия PIP. Если ничего не происходит, значит, PIP у вас не установлен. Исправим это:</p>
37
python3 -m pip --version<p>В ответ на экран выведется версия PIP. Если ничего не происходит, значит, PIP у вас не установлен. Исправим это:</p>
38
python -m ensurepip --default-pip<p>Снова проверим, появился ли в системе менеджер пакетов. Если всё равно что-то не получается, попробуйте найти решение проблемы на <a>Stack Overflow</a> - или обратитесь к астрологу :)</p>
38
python -m ensurepip --default-pip<p>Снова проверим, появился ли в системе менеджер пакетов. Если всё равно что-то не получается, попробуйте найти решение проблемы на <a>Stack Overflow</a> - или обратитесь к астрологу :)</p>
39
<p>Теперь поставим сам Flask. Делается это очень просто:</p>
39
<p>Теперь поставим сам Flask. Делается это очень просто:</p>
40
pip install Flask<p>Начнётся процесс загрузки Flask, после которого он будет готов к использованию. Если вам нужна конкретная версия Flask, установить её можно, указав её номер с помощью дополнительного параметра ==<version>.</p>
40
pip install Flask<p>Начнётся процесс загрузки Flask, после которого он будет готов к использованию. Если вам нужна конкретная версия Flask, установить её можно, указав её номер с помощью дополнительного параметра ==<version>.</p>
41
pip install Flask==<version><p>Например, мы можем установить версию 2.0.1:</p>
41
pip install Flask==<version><p>Например, мы можем установить версию 2.0.1:</p>
42
pip install Flask==2.0.1<p>Чтобы проверить, работает ли Flask, введём следующую команду:</p>
42
pip install Flask==2.0.1<p>Чтобы проверить, работает ли Flask, введём следующую команду:</p>
43
pip show flask<p>или создадим Python-файл и впишем туда такую строку:</p>
43
pip show flask<p>или создадим Python-файл и впишем туда такую строку:</p>
44
import flask<p>Теперь запустим интерпретатор и убедимся, что программа исполняется без ошибок.</p>
44
import flask<p>Теперь запустим интерпретатор и убедимся, что программа исполняется без ошибок.</p>
45
<p>Приступим к коду. Для начала нам понадобится основа для приложения. Создадим новый файл с именем app.py. Это и будет наше Flask-приложение.</p>
45
<p>Приступим к коду. Для начала нам понадобится основа для приложения. Создадим новый файл с именем app.py. Это и будет наше Flask-приложение.</p>
46
<p>На первом этапе импортируем класс Flask из библиотеки Flask:</p>
46
<p>На первом этапе импортируем класс Flask из библиотеки Flask:</p>
47
from flask import Flask<p>Затем создадим экземпляр класса Flask:</p>
47
from flask import Flask<p>Затем создадим экземпляр класса Flask:</p>
48
app = Flask(__name__)<p>Здесь мы передаем аргумент __name__ конструктору класса, этот аргумент скажет Flask, где находится наше приложение. Так Flask сможет определить местоположение шаблонов и статических файлов, о которых речь пойдёт дальше. Если вы ещё не особо знакомы с классами в Python, советуем прочитать нашу статью об <a>объектно-ориентированном программировании на Python</a>.</p>
48
app = Flask(__name__)<p>Здесь мы передаем аргумент __name__ конструктору класса, этот аргумент скажет Flask, где находится наше приложение. Так Flask сможет определить местоположение шаблонов и статических файлов, о которых речь пойдёт дальше. Если вы ещё не особо знакомы с классами в Python, советуем прочитать нашу статью об <a>объектно-ориентированном программировании на Python</a>.</p>
49
<p>Весь бэкенд строится на маршрутах - или URL-адресах. Они помогают задавать удобную структуру и понятное поведение веб-приложениям.</p>
49
<p>Весь бэкенд строится на маршрутах - или URL-адресах. Они помогают задавать удобную структуру и понятное поведение веб-приложениям.</p>
50
<p>Для пользователя маршруты - это отдельные "вкладки" на сайте. Например, если зайти на сайт Skillbox, откроется его главная страница<a>www.skillbox.ru</a>. А если кликнуть на любой курс, мы перейдём на другую страницу сайта с другим URL-адресом, таким как<a>www.skillbox.ru/course/profession-python</a>. Видим, что к адресу нашего сайта добавился текст: /course/profession-python/. Эта "приписка" и перенесла нас на другую страницу с другим содержимым. Получается, маршруты позволяют создавать разные страницы с разным наполнением в рамках одного сайта.</p>
50
<p>Для пользователя маршруты - это отдельные "вкладки" на сайте. Например, если зайти на сайт Skillbox, откроется его главная страница<a>www.skillbox.ru</a>. А если кликнуть на любой курс, мы перейдём на другую страницу сайта с другим URL-адресом, таким как<a>www.skillbox.ru/course/profession-python</a>. Видим, что к адресу нашего сайта добавился текст: /course/profession-python/. Эта "приписка" и перенесла нас на другую страницу с другим содержимым. Получается, маршруты позволяют создавать разные страницы с разным наполнением в рамках одного сайта.</p>
51
<p>Чтобы задать маршрут во Flask, нужно написать следующее:</p>
51
<p>Чтобы задать маршрут во Flask, нужно написать следующее:</p>
52
@app.route('/') def hello_world(): return 'Hello, World!'<p>Так мы создали URL-адрес главной страницы сайта. Например, для Skillbox главной страницей будет<a>www.skillbox.ru</a>. Тут мы мысленно можем дописать слеш: www.skillbox.ru/.</p>
52
@app.route('/') def hello_world(): return 'Hello, World!'<p>Так мы создали URL-адрес главной страницы сайта. Например, для Skillbox главной страницей будет<a>www.skillbox.ru</a>. Тут мы мысленно можем дописать слеш: www.skillbox.ru/.</p>
53
<p>Сам маршрут задаётся в строке @app.route('/'). Внутрь круглых скобочек мы по ходу статьи будем вписывать разные маршруты, а пока нам хватит стандартного.</p>
53
<p>Сам маршрут задаётся в строке @app.route('/'). Внутрь круглых скобочек мы по ходу статьи будем вписывать разные маршруты, а пока нам хватит стандартного.</p>
54
<p>Внутрь маршрута мы поместили функцию hello_world(), которая будет выполняться при обращении к корневому URL, или главной странице нашего сайта (ведь наш маршрут ведёт именно на неё). Функция возвращает строку Hello, World! в браузере.</p>
54
<p>Внутрь маршрута мы поместили функцию hello_world(), которая будет выполняться при обращении к корневому URL, или главной странице нашего сайта (ведь наш маршрут ведёт именно на неё). Функция возвращает строку Hello, World! в браузере.</p>
55
<p>Теперь нам нужно запустить приложение:</p>
55
<p>Теперь нам нужно запустить приложение:</p>
56
if __name__ == '__main__': app.run()<p>Этот код гарантирует, что сервер Flask будет запущен только в том случае, если файл app.py был запущен напрямую, а не импортирован как модуль.</p>
56
if __name__ == '__main__': app.run()<p>Этот код гарантирует, что сервер Flask будет запущен только в том случае, если файл app.py был запущен напрямую, а не импортирован как модуль.</p>
57
<p>Сохраняем файл app.py и запускаем его с помощью команды в консоли:</p>
57
<p>Сохраняем файл app.py и запускаем его с помощью команды в консоли:</p>
58
python app.py<p>После запуска вы должны увидеть сообщение о том, что сервер Flask был запущен:</p>
58
python app.py<p>После запуска вы должны увидеть сообщение о том, что сервер Flask был запущен:</p>
59
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)<p>Чтобы взглянуть на работу нашего приложения, нужно перейти по адресу, который был указан в консоли - http://127.0.0.1:5000/. Вот что мы там увидим:</p>
59
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)<p>Чтобы взглянуть на работу нашего приложения, нужно перейти по адресу, который был указан в консоли - http://127.0.0.1:5000/. Вот что мы там увидим:</p>
60
<em>Скриншот: Skillbox Media</em><p>Ликуем - у нас всё получилось, сайт работает. Дальше будем усложнять наше приложение и начнём создавать блог.</p>
60
<em>Скриншот: Skillbox Media</em><p>Ликуем - у нас всё получилось, сайт работает. Дальше будем усложнять наше приложение и начнём создавать блог.</p>
61
<p>Чтобы создать блог, одним простым приложением уже не обойтись, придётся научиться использовать HTML-шаблоны и подключать базу данных.</p>
61
<p>Чтобы создать блог, одним простым приложением уже не обойтись, придётся научиться использовать HTML-шаблоны и подключать базу данных.</p>
62
<p>В дальнейшем мы будем использовать наш базовый код из предыдущего раздела. Полностью он выглядит так:</p>
62
<p>В дальнейшем мы будем использовать наш базовый код из предыдущего раздела. Полностью он выглядит так:</p>
63
<p>app.py</p>
63
<p>app.py</p>
64
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()<p>HTML-шаблоны - это файлы, которые задают структуру и содержимое страниц сайта. Шаблоны упрощают жизнь программистам - им не приходится десятки раз писать один и тот же HTML-код, ведь его можно просто взять и… шаблонизировать.</p>
64
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()<p>HTML-шаблоны - это файлы, которые задают структуру и содержимое страниц сайта. Шаблоны упрощают жизнь программистам - им не приходится десятки раз писать один и тот же HTML-код, ведь его можно просто взять и… шаблонизировать.</p>
65
<p><strong>Ещё немного об HTML-шаблонах</strong></p>
65
<p><strong>Ещё немного об HTML-шаблонах</strong></p>
66
<p>HTML-шаблоны позволяют создавать динамические веб-страницы с помощью заготовленных блоков, которые мы можем настраивать и использовать повторно. Шаблоны помогают избежать дублирования кода и облегчают поддержку приложения, так как мы можем легко изменять отображение страниц, не затрагивая внутреннюю логику. Например, в нашем медиа о программировании оформление всех статей очень похоже: структура, внешние по отношению к статье элементы и тому подобное - это возможно как раз благодаря шаблонам.</p>
66
<p>HTML-шаблоны позволяют создавать динамические веб-страницы с помощью заготовленных блоков, которые мы можем настраивать и использовать повторно. Шаблоны помогают избежать дублирования кода и облегчают поддержку приложения, так как мы можем легко изменять отображение страниц, не затрагивая внутреннюю логику. Например, в нашем медиа о программировании оформление всех статей очень похоже: структура, внешние по отношению к статье элементы и тому подобное - это возможно как раз благодаря шаблонам.</p>
67
<p>Например, можно использовать один и тот же базовый шаблон для всех страниц сайта и переопределять блоки контента для каждой страницы. В таком случае, если нам понадобится изменить дизайн всего сайта, мы можем изменить только базовый шаблон, а все страницы обновятся автоматически, без дополнительных усилий.</p>
67
<p>Например, можно использовать один и тот же базовый шаблон для всех страниц сайта и переопределять блоки контента для каждой страницы. В таком случае, если нам понадобится изменить дизайн всего сайта, мы можем изменить только базовый шаблон, а все страницы обновятся автоматически, без дополнительных усилий.</p>
68
<p>Если вы не знаете, как писать HTML-код, советуем прочитать нашу<a>статью об HTML</a>. А теперь создадим HTML-шаблоны: выделим под них папку templates и добавим в неё файл base.html со следующим содержимым:</p>
68
<p>Если вы не знаете, как писать HTML-код, советуем прочитать нашу<a>статью об HTML</a>. А теперь создадим HTML-шаблоны: выделим под них папку templates и добавим в неё файл base.html со следующим содержимым:</p>
69
<!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body> </html><p>Этот шаблон будет отправной точкой для всех остальных HTML-страниц. В нём мы прописали заголовок:</p>
69
<!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body> </html><p>Этот шаблон будет отправной точкой для всех остальных HTML-страниц. В нём мы прописали заголовок:</p>
70
<head> <title>{% block title %}{% endblock %}</title> </head><p>и основной контент (тело) страницы:</p>
70
<head> <title>{% block title %}{% endblock %}</title> </head><p>и основной контент (тело) страницы:</p>
71
<body> {% block content %}{% endblock %} </body><p>Вы, наверное, уже обратили внимание на странные элементы, а точнее теги {% block %} и {% endblock %}. Они как раз нужны, чтобы динамически добавлять туда новые элементы: другие HTML-блоки, JavaScript-код и тому подобное.</p>
71
<body> {% block content %}{% endblock %} </body><p>Вы, наверное, уже обратили внимание на странные элементы, а точнее теги {% block %} и {% endblock %}. Они как раз нужны, чтобы динамически добавлять туда новые элементы: другие HTML-блоки, JavaScript-код и тому подобное.</p>
72
<p>Теперь давайте создадим второй шаблон и назовём его index.html. Он будет наследовать элементы базового шаблона:</p>
72
<p>Теперь давайте создадим второй шаблон и назовём его index.html. Он будет наследовать элементы базового шаблона:</p>
73
{% extends "base.html" %} {% block title %}Home{% endblock %} {% block content %} <h1>Welcome to my website!</h1> <p>This is the homepage.</p> {% endblock %}<p>Этот шаблон переопределяет заголовок страницы и определяет контент, который будет отображаться на домашней странице: заголовок страницы Home и немного текста.</p>
73
{% extends "base.html" %} {% block title %}Home{% endblock %} {% block content %} <h1>Welcome to my website!</h1> <p>This is the homepage.</p> {% endblock %}<p>Этот шаблон переопределяет заголовок страницы и определяет контент, который будет отображаться на домашней странице: заголовок страницы Home и немного текста.</p>
74
<p>Пришло время воспользоваться шаблонами. Изменим файл app.py и импортируем функцию render_template из библиотеки Flask, которая позволяет работать с шаблонами:</p>
74
<p>Пришло время воспользоваться шаблонами. Изменим файл app.py и импортируем функцию render_template из библиотеки Flask, которая позволяет работать с шаблонами:</p>
75
from flask import Flask, render_template<p>Изменим маршрут главной страницы и используем в нём новую функцию, чтобы отобразить шаблон index.html:</p>
75
from flask import Flask, render_template<p>Изменим маршрут главной страницы и используем в нём новую функцию, чтобы отобразить шаблон index.html:</p>
76
@app.route('/') def index(): return render_template('index.html')<p>Сохраним изменения в файлах app.py, index.html и base.html, а затем снова запустим наше приложение:</p>
76
@app.route('/') def index(): return render_template('index.html')<p>Сохраним изменения в файлах app.py, index.html и base.html, а затем снова запустим наше приложение:</p>
77
-
python app.py<p>Открываем браузер и переходим по а��ресу http://127.0.0.1:5000/. У нас отобразится содержимое шаблона index.html:</p>
77
+
python app.py<p>Открываем браузер и переходим по адресу http://127.0.0.1:5000/. У нас отобразится содержимое шаблона index.html:</p>
78
<em>Скриншот: Skillbox Media</em><p>Чтобы вести блог, нужно куда-то сохранять все посты, картинки, видео и тому подобное. Куда-то - это в базу данных. Баз данных существует немало, мы даже написали про них<a>отдельную статью</a>. Но если коротко - база данных нужна, чтобы удобно хранить, обрабатывать и сохранять данные.</p>
78
<em>Скриншот: Skillbox Media</em><p>Чтобы вести блог, нужно куда-то сохранять все посты, картинки, видео и тому подобное. Куда-то - это в базу данных. Баз данных существует немало, мы даже написали про них<a>отдельную статью</a>. Но если коротко - база данных нужна, чтобы удобно хранить, обрабатывать и сохранять данные.</p>
79
<p>Мы будем использовать базу данных SQLite, потому что она занимает совсем немного места, легка в освоении и вообще клёвая. А главный плюс - её не нужно скачивать отдельно, потому что она сразу есть в Python.</p>
79
<p>Мы будем использовать базу данных SQLite, потому что она занимает совсем немного места, легка в освоении и вообще клёвая. А главный плюс - её не нужно скачивать отдельно, потому что она сразу есть в Python.</p>
80
<p>Займёмся привычным делом - импортируем модуль SQLite3:</p>
80
<p>Займёмся привычным делом - импортируем модуль SQLite3:</p>
81
import sqlite3<p>Теперь мы можем создать функцию, которая будет подключать нас к базе данных и возвращать объект подключения:</p>
81
import sqlite3<p>Теперь мы можем создать функцию, которая будет подключать нас к базе данных и возвращать объект подключения:</p>
82
def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn<p>Эта функция создаст подключение к базе данных database.db и установит режим получения результатов запросов в виде словаря Row, что позволит обращаться к столбцам базы данных по их именам.</p>
82
def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn<p>Эта функция создаст подключение к базе данных database.db и установит режим получения результатов запросов в виде словаря Row, что позволит обращаться к столбцам базы данных по их именам.</p>
83
<p>Другими словами - наши данные будут храниться в виде таблицы, где каждая строка - уникальный пост, а каждый столбец (или ячейка) - информация об этом посте, например, сам текст, количество лайков, автор и так далее.</p>
83
<p>Другими словами - наши данные будут храниться в виде таблицы, где каждая строка - уникальный пост, а каждый столбец (или ячейка) - информация об этом посте, например, сам текст, количество лайков, автор и так далее.</p>
84
<p>Но ещё нам понадобится функция, которая будет закрывать подключение к базе данных:</p>
84
<p>Но ещё нам понадобится функция, которая будет закрывать подключение к базе данных:</p>
85
def close_db_connection(conn): conn.close()<p>Теперь мы можем использовать эти функции для выполнения запросов к базе данных внутри нашего приложения. Обновим функцию index(), чтобы она получала все записи из таблицы posts:</p>
85
def close_db_connection(conn): conn.close()<p>Теперь мы можем использовать эти функции для выполнения запросов к базе данных внутри нашего приложения. Обновим функцию index(), чтобы она получала все записи из таблицы posts:</p>
86
@app.route('/') def index(): conn = get_db_connection() posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts)<p>Здесь мы получаем подключение к базе данных с помощью функции get_db_connection(), выполняем запрос к таблице posts и получаем все записи, используя метод fetchall(). Затем мы закрываем подключение с помощью функции close_db_connection() и передаём полученные записи в шаблон index.html с помощью функции render_template().</p>
86
@app.route('/') def index(): conn = get_db_connection() posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts)<p>Здесь мы получаем подключение к базе данных с помощью функции get_db_connection(), выполняем запрос к таблице posts и получаем все записи, используя метод fetchall(). Затем мы закрываем подключение с помощью функции close_db_connection() и передаём полученные записи в шаблон index.html с помощью функции render_template().</p>
87
<p>Но перед тем как использовать базу данных, её нужно инициализировать и создать таблицу posts.</p>
87
<p>Но перед тем как использовать базу данных, её нужно инициализировать и создать таблицу posts.</p>
88
<p>У нашего учебного поста будет три поля с данными: уникальный идентификатор (ID), заголовок и текст поста. Назовём их соответственно - id, title и content:</p>
88
<p>У нашего учебного поста будет три поля с данными: уникальный идентификатор (ID), заголовок и текст поста. Назовём их соответственно - id, title и content:</p>
89
def init_db(): conn = get_db_connection() conn.execute('CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)') conn.close()<p>Функция execute() создаст нам таблицу posts с полями id, title и content. У каждого из этих полей будет собственный тип данных: целое число, строка и строка соответственно.</p>
89
def init_db(): conn = get_db_connection() conn.execute('CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)') conn.close()<p>Функция execute() создаст нам таблицу posts с полями id, title и content. У каждого из этих полей будет собственный тип данных: целое число, строка и строка соответственно.</p>
90
<p>Мы задали полю id специальный параметр AUTOINCREMENT, чтобы при добавлении нового поста его айдишник автоматически увеличивался на 1. А параметр PRIMARY KEY нужен для уточнения, что строки в таблице уникальные и не пустые.</p>
90
<p>Мы задали полю id специальный параметр AUTOINCREMENT, чтобы при добавлении нового поста его айдишник автоматически увеличивался на 1. А параметр PRIMARY KEY нужен для уточнения, что строки в таблице уникальные и не пустые.</p>
91
<p>Инициализировать базу данных нужно в самом начале. Во Flask мы можем сделать это перед первым запросов к базе данных с помощью следующего кода:</p>
91
<p>Инициализировать базу данных нужно в самом начале. Во Flask мы можем сделать это перед первым запросов к базе данных с помощью следующего кода:</p>
92
@app.before_first_request def before_first_request(): init_db()<p>Здесь мы используем декоратор @app.before_first_request, который указывает, что функция before_first_request() вызывается перед тем, как запустится рендер шаблонов.</p>
92
@app.before_first_request def before_first_request(): init_db()<p>Здесь мы используем декоратор @app.before_first_request, который указывает, что функция before_first_request() вызывается перед тем, как запустится рендер шаблонов.</p>
93
<p>Готово - наше приложение подключено к базе данных SQLite, а мы можем переходить дальше. Но сперва убедимся, что всё работает. Сохраняем изменения и запускаем приложение:</p>
93
<p>Готово - наше приложение подключено к базе данных SQLite, а мы можем переходить дальше. Но сперва убедимся, что всё работает. Сохраняем изменения и запускаем приложение:</p>
94
python app.py<p>Если перейти по адресу http://127.0.0.1:5000/, то может показаться, что ничего не изменилось. Но если посмотреть в папку с файлами, то можно увидеть там новый файл - database.db. Это и есть наша база данных.</p>
94
python app.py<p>Если перейти по адресу http://127.0.0.1:5000/, то может показаться, что ничего не изменилось. Но если посмотреть в папку с файлами, то можно увидеть там новый файл - database.db. Это и есть наша база данных.</p>
95
<em>Скриншот: Skillbox Media</em><p>Настало время сделать новый и прекрасный шаблон - для постов. Он будет простым: заголовок, текст и ссылка на главную страницу. Создадим в папке templates файл post.html и добавим следующий код:</p>
95
<em>Скриншот: Skillbox Media</em><p>Настало время сделать новый и прекрасный шаблон - для постов. Он будет простым: заголовок, текст и ссылка на главную страницу. Создадим в папке templates файл post.html и добавим следующий код:</p>
96
<!DOCTYPE html> <html> <head> <title>{{ post.title }}</title> </head> <body> <h1>{{ post.title }}</h1> <p>{{ post.content }}</p> <a href="{{ url_for('index') }}">Back to index</a> </body> </html><p>В момент рендеринга в эту HTML-страницу мы будем передавать пост, который достали из базы данных. У этого поста будут заголовок (title) и основной текст (content), а всё вместе будет лежать внутри объекта post.</p>
96
<!DOCTYPE html> <html> <head> <title>{{ post.title }}</title> </head> <body> <h1>{{ post.title }}</h1> <p>{{ post.content }}</p> <a href="{{ url_for('index') }}">Back to index</a> </body> </html><p>В момент рендеринга в эту HTML-страницу мы будем передавать пост, который достали из базы данных. У этого поста будут заголовок (title) и основной текст (content), а всё вместе будет лежать внутри объекта post.</p>
97
<p>Пока всё просто, но обратите внимание на новую функцию - url_for(). Она позволяет перейти на другой маршрут и отрисовать другую HTML-страницу. В нашем случае, если мы нажмём на ссылку<strong>Back to index</strong>, запустится функция index(), которая перенаправит нас на главную страницу (маршрут /) и отрисует шаблон index.html.</p>
97
<p>Пока всё просто, но обратите внимание на новую функцию - url_for(). Она позволяет перейти на другой маршрут и отрисовать другую HTML-страницу. В нашем случае, если мы нажмём на ссылку<strong>Back to index</strong>, запустится функция index(), которая перенаправит нас на главную страницу (маршрут /) и отрисует шаблон index.html.</p>
98
<p>Теперь нам нужно сделать новый маршрут под один из постов в файле app.py:</p>
98
<p>Теперь нам нужно сделать новый маршрут под один из постов в файле app.py:</p>
99
@app.route('/<int:post_id>') def get_post(post_id): conn = get_db_connection() post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post)<p>Тут уже всё немного сложнее, но обо всём по порядку:</p>
99
@app.route('/<int:post_id>') def get_post(post_id): conn = get_db_connection() post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post)<p>Тут уже всё немного сложнее, но обо всём по порядку:</p>
100
<ul><li>@app.route('/<int:post_id>') - задаём новый маршрут. Он будет выглядеть так: www.oursite.com/1. Единица будет указывать на индекс поста из базы данных. <int:post_id> - это ещё одно указание на то, что индекс поста должен быть целым числом (int), а не, например, строкой.</li>
100
<ul><li>@app.route('/<int:post_id>') - задаём новый маршрут. Он будет выглядеть так: www.oursite.com/1. Единица будет указывать на индекс поста из базы данных. <int:post_id> - это ещё одно указание на то, что индекс поста должен быть целым числом (int), а не, например, строкой.</li>
101
<li>get_post(post_id) - передаём айдишник поста, который как раз и "встанет" в URL-адрес и добавится к запросу к базе данных.</li>
101
<li>get_post(post_id) - передаём айдишник поста, который как раз и "встанет" в URL-адрес и добавится к запросу к базе данных.</li>
102
<li>post = conn.execute ('SELECT * FROM posts WHERE id =? ', (post_id,)).fetchone() - запрашиваем из базы данных пост по нашему айдишнику и берём одну строку функцией fetchone().</li>
102
<li>post = conn.execute ('SELECT * FROM posts WHERE id =? ', (post_id,)).fetchone() - запрашиваем из базы данных пост по нашему айдишнику и берём одну строку функцией fetchone().</li>
103
<li>return render_template ('post.html', post=post) - рендерим HTML-шаблон и передаём туда полученный пост.</li>
103
<li>return render_template ('post.html', post=post) - рендерим HTML-шаблон и передаём туда полученный пост.</li>
104
</ul><p>Давайте убедимся, что всё работает корректно и страница отрисовывается. Временно напишем "костыль" и вручную добавим пост (исключительно для проверки):</p>
104
</ul><p>Давайте убедимся, что всё работает корректно и страница отрисовывается. Временно напишем "костыль" и вручную добавим пост (исключительно для проверки):</p>
105
@app.route('/<int:post_id>') def get_post(post_id): conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES ("Random Title", "Lorem ipsum dolor sit amet consectetur adipiscing elit")') post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post)<p>Наш "костыль" - это четвёртая строка. Здесь мы сами вставляем новую строку в базу данных с помощью запроса INSERT. Теперь перейдём по адресу http://127.0.0.1:5000/1:</p>
105
@app.route('/<int:post_id>') def get_post(post_id): conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES ("Random Title", "Lorem ipsum dolor sit amet consectetur adipiscing elit")') post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post)<p>Наш "костыль" - это четвёртая строка. Здесь мы сами вставляем новую строку в базу данных с помощью запроса INSERT. Теперь перейдём по адресу http://127.0.0.1:5000/1:</p>
106
<em>Скриншот: Skillbox Media</em><p>Круто - всё отрисовалось, кнопка "Назад" работает! Идём дальше и не забываем удалить "костыль".</p>
106
<em>Скриншот: Skillbox Media</em><p>Круто - всё отрисовалось, кнопка "Назад" работает! Идём дальше и не забываем удалить "костыль".</p>
107
<p>Дополним файл index.html и создадим список, в котором будут находиться все посты из базы данных:</p>
107
<p>Дополним файл index.html и создадим список, в котором будут находиться все посты из базы данных:</p>
108
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html><p>Главный движ у нас происходит в теге <ul>: мы идём по всем элементам списка posts и для каждого создаём заголовок со ссылкой на пост. При нажатии на ссылку вызывается функция url_for(), а ей передаётся функция-рендер get_post() и ID поста.</p>
108
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html><p>Главный движ у нас происходит в теге <ul>: мы идём по всем элементам списка posts и для каждого создаём заголовок со ссылкой на пост. При нажатии на ссылку вызывается функция url_for(), а ей передаётся функция-рендер get_post() и ID поста.</p>
109
<p>Ещё мы немного декорировали заголовок самой страницы. Можете сравнить с прошлой версией.</p>
109
<p>Ещё мы немного декорировали заголовок самой страницы. Можете сравнить с прошлой версией.</p>
110
<p>Если перезапустить приложение, то мы не увидим новых постов, потому что их просто нет в базе данных. Но можно опять заполнить всё вручную. Изменим функцию index():</p>
110
<p>Если перезапустить приложение, то мы не увидим новых постов, потому что их просто нет в базе данных. Но можно опять заполнить всё вручную. Изменим функцию index():</p>
111
@app.route('/') def index(): conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES ("Why I love Flask", "This is so cool!!!")') conn.execute('INSERT INTO posts (title, content) VALUES ("Cats >> Dogs", "It was a joke because they are all so adorable.")') posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts)<p>Видим результат:</p>
111
@app.route('/') def index(): conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES ("Why I love Flask", "This is so cool!!!")') conn.execute('INSERT INTO posts (title, content) VALUES ("Cats >> Dogs", "It was a joke because they are all so adorable.")') posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts)<p>Видим результат:</p>
112
<em>Скриншот: Skillbox Media</em><p>Правда, если перейти по ссылке, то ничего не отобразится, но и это мы со временем исправим.</p>
112
<em>Скриншот: Skillbox Media</em><p>Правда, если перейти по ссылке, то ничего не отобразится, но и это мы со временем исправим.</p>
113
<p>Отображение постов работает, осталось добавить возможность создавать новые посты. Для этого создадим новый HTML-шаблон и метод для рендеринга.</p>
113
<p>Отображение постов работает, осталось добавить возможность создавать новые посты. Для этого создадим новый HTML-шаблон и метод для рендеринга.</p>
114
<p>В папке templates создаём новый файл add_post.html:</p>
114
<p>В папке templates создаём новый файл add_post.html:</p>
115
{% extends 'base.html' %} {% block content %} <h1>Add New Post</h1> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flashes"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <form method="post"> <label for="title">Title</label><br> <input type="text" id="title" name="title"><br> <label for="content">Content</label><br> <textarea id="content" name="content"></textarea><br> <input type="submit" value="Submit"> </form> {% endblock %}<p>Он будет наследовать файл base.html, а внутри содержать форму, где пользователь указывает заголовок и тело поста. Если он вдруг введёт некорректные данные, то мы покажем ему сообщение об ошибке с помощью флеш-сообщений (когда некорректно заполненное поле подсвечивается красным и у вас нет возможности отправить заполненную форму).</p>
115
{% extends 'base.html' %} {% block content %} <h1>Add New Post</h1> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flashes"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <form method="post"> <label for="title">Title</label><br> <input type="text" id="title" name="title"><br> <label for="content">Content</label><br> <textarea id="content" name="content"></textarea><br> <input type="submit" value="Submit"> </form> {% endblock %}<p>Он будет наследовать файл base.html, а внутри содержать форму, где пользователь указывает заголовок и тело поста. Если он вдруг введёт некорректные данные, то мы покажем ему сообщение об ошибке с помощью флеш-сообщений (когда некорректно заполненное поле подсвечивается красным и у вас нет возможности отправить заполненную форму).</p>
116
<p>Теперь добавим новый маршрут в файл app.py:</p>
116
<p>Теперь добавим новый маршрут в файл app.py:</p>
117
@app.route('/new', methods=['GET', 'POST']) def new_post(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] 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('add_post.html')<p>Этот маршрут обрабатывает GET- и POST-запросы по адресу /new. Если запрос выполняется методом GET, то функция просто отображает форму для ввода данных с помощью шаблона add_post.html. А если запрос выполняется методом POST, мы достаём заголовок и тело поста из формы, а затем добавляем их в базу данных. В конце - редиректим (то есть перенаправляем) пользователя обратно на главную.</p>
117
@app.route('/new', methods=['GET', 'POST']) def new_post(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] 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('add_post.html')<p>Этот маршрут обрабатывает GET- и POST-запросы по адресу /new. Если запрос выполняется методом GET, то функция просто отображает форму для ввода данных с помощью шаблона add_post.html. А если запрос выполняется методом POST, мы достаём заголовок и тело поста из формы, а затем добавляем их в базу данных. В конце - редиректим (то есть перенаправляем) пользователя обратно на главную.</p>
118
<p>Подробнее об HTTP-запросах вы можете прочитать в <a>нашей статье</a>.</p>
118
<p>Подробнее об HTTP-запросах вы можете прочитать в <a>нашей статье</a>.</p>
119
<p>Чтобы все эти функции работали, мы также должны их импортировать в начале файла:</p>
119
<p>Чтобы все эти функции работали, мы также должны их импортировать в начале файла:</p>
120
from flask import Flask, render_template, request, redirect, url_for<p>Теперь давайте сохраним свои наработки и запустим приложение:</p>
120
from flask import Flask, render_template, request, redirect, url_for<p>Теперь давайте сохраним свои наработки и запустим приложение:</p>
121
python app.py<p>Переходим по адресу http://127.0.0.1:5000/ и видим, что опять ничего не изменилось:</p>
121
python app.py<p>Переходим по адресу http://127.0.0.1:5000/ и видим, что опять ничего не изменилось:</p>
122
<em>Скриншот: Skillbox Media</em><p>Попробуем перейти по адресу http://127.0.0.1:5000/new:</p>
122
<em>Скриншот: Skillbox Media</em><p>Попробуем перейти по адресу http://127.0.0.1:5000/new:</p>
123
<em>Скриншот: Skillbox Media</em><p>Ура! Мы видим нашу форму! Давайте впишем туда что-нибудь и нажмём<strong>Submit</strong>:</p>
123
<em>Скриншот: Skillbox Media</em><p>Ура! Мы видим нашу форму! Давайте впишем туда что-нибудь и нажмём<strong>Submit</strong>:</p>
124
<em>Скриншот: Skillbox Media</em><p>Нас перекинуло на главную страницу - и на ней находится наш новый пост. Ура! Всё наконец-то работает!</p>
124
<em>Скриншот: Skillbox Media</em><p>Нас перекинуло на главную страницу - и на ней находится наш новый пост. Ура! Всё наконец-то работает!</p>
125
<em>Скриншот: Skillbox Media</em><p>К тому же теперь мы можем перейти по ссылке и посмотреть пост целиком:</p>
125
<em>Скриншот: Skillbox Media</em><p>К тому же теперь мы можем перейти по ссылке и посмотреть пост целиком:</p>
126
<em>Скриншот: Skillbox Media</em><p>Но всё ещё есть проблема - нужно добавить кнопку<strong>Add new post</strong>, чтобы каждый раз не добавлять пост, вбивая в адресную строку наш URL. Для этого нам нужно добавить всего одну строчку в файл index.html:</p>
126
<em>Скриншот: Skillbox Media</em><p>Но всё ещё есть проблема - нужно добавить кнопку<strong>Add new post</strong>, чтобы каждый раз не добавлять пост, вбивая в адресную строку наш URL. Для этого нам нужно добавить всего одну строчку в файл index.html:</p>
127
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <a href="{{ url_for('new_post') }}"><button>Add New Post</button></a> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html><p>Мы создали кнопку, нажав которую, мы перейдём на страницу со ссылкой для создания нового поста. Проверяем:</p>
127
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <a href="{{ url_for('new_post') }}"><button>Add New Post</button></a> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html><p>Мы создали кнопку, нажав которую, мы перейдём на страницу со ссылкой для создания нового поста. Проверяем:</p>
128
<em>Скриншот: Skillbox Media</em><p>Кликаем - переходим куда нужно:</p>
128
<em>Скриншот: Skillbox Media</em><p>Кликаем - переходим куда нужно:</p>
129
<em>Скриншот: Skillbox Media</em><p>Добавили новый пост:</p>
129
<em>Скриншот: Skillbox Media</em><p>Добавили новый пост:</p>
130
<em>Скриншот: Skillbox Media</em><p>Наше приложение приобрело следующую структуру:</p>
130
<em>Скриншот: Skillbox Media</em><p>Наше приложение приобрело следующую структуру:</p>
131
Blog Flask/ |-- templates/ | |-- index.html | |-- base.html | |-- post.html | |-- add_post.html |-- app.py |-- database.db<p>В папке templates хранятся HTML-шаблоны: index.html, add_post.html, post.html и base.html. Файл app.py содержит основной код приложения Flask, в котором определены маршруты и функции для работы с базой данных. А файл database.db - это база данных SQLite.</p>
131
Blog Flask/ |-- templates/ | |-- index.html | |-- base.html | |-- post.html | |-- add_post.html |-- app.py |-- database.db<p>В папке templates хранятся HTML-шаблоны: index.html, add_post.html, post.html и base.html. Файл app.py содержит основной код приложения Flask, в котором определены маршруты и функции для работы с базой данных. А файл database.db - это база данных SQLite.</p>
132
<p>Полный код каждого файла можно посмотреть ниже:</p>
132
<p>Полный код каждого файла можно посмотреть ниже:</p>
133
<p>index.html:</p>
133
<p>index.html:</p>
134
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <a href="{{ url_for('new_post') }}"><button>Add New Post</button></a> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html><p>base.html:</p>
134
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <a href="{{ url_for('new_post') }}"><button>Add New Post</button></a> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html><p>base.html:</p>
135
<!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body> </html><p>post.html:</p>
135
<!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body> </html><p>post.html:</p>
136
<!DOCTYPE html> <html> <head> <title>{{ post.title }}</title> </head> <body> <h1>{{ post.title }}</h1> <p>{{ post.content }}</p> <a href="{{ url_for('index') }}">Back to index</a> </body> </html><p>add_post.html:</p>
136
<!DOCTYPE html> <html> <head> <title>{{ post.title }}</title> </head> <body> <h1>{{ post.title }}</h1> <p>{{ post.content }}</p> <a href="{{ url_for('index') }}">Back to index</a> </body> </html><p>add_post.html:</p>
137
{% extends 'base.html' %} {% block content %} <h1>Add New Post</h1> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flashes"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <form method="post"> <label for="title">Title</label><br> <input type="text" id="title" name="title"><br> <label for="content">Content</label><br> <textarea id="content" name="content"></textarea><br> <input type="submit" value="Submit"> </form> {% endblock %}<p>app.py:</p>
137
{% extends 'base.html' %} {% block content %} <h1>Add New Post</h1> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flashes"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <form method="post"> <label for="title">Title</label><br> <input type="text" id="title" name="title"><br> <label for="content">Content</label><br> <textarea id="content" name="content"></textarea><br> <input type="submit" value="Submit"> </form> {% endblock %}<p>app.py:</p>
138
from flask import Flask, render_template, request, redirect, url_for import sqlite3 app = Flask(__name__) def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn def close_db_connection(conn): conn.close() def init_db(): conn = get_db_connection() conn.execute('CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)') conn.close() @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 get_post(post_id): conn = get_db_connection() post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post) @app.route('/new', methods=['GET', 'POST']) def new_post(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] 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('add_post.html') @app.before_first_request def before_first_request(): init_db() if __name__ == '__main__': app.run()<p>Наш блог полностью готов к работе. В него можно добавлять новые функции (например, редактирование постов или систему авторизации), а также применять CSS-стили, чтобы он выглядел красивее. Но главное - мы заложили основу для бэкенда и разобрались, как это сделать на Flask.</p>
138
from flask import Flask, render_template, request, redirect, url_for import sqlite3 app = Flask(__name__) def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn def close_db_connection(conn): conn.close() def init_db(): conn = get_db_connection() conn.execute('CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)') conn.close() @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 get_post(post_id): conn = get_db_connection() post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post) @app.route('/new', methods=['GET', 'POST']) def new_post(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] 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('add_post.html') @app.before_first_request def before_first_request(): init_db() if __name__ == '__main__': app.run()<p>Наш блог полностью готов к работе. В него можно добавлять новые функции (например, редактирование постов или систему авторизации), а также применять CSS-стили, чтобы он выглядел красивее. Но главное - мы заложили основу для бэкенда и разобрались, как это сделать на Flask.</p>
139
<p>Вот некоторые важные вещи, которые стоит помнить при работе с фреймворком Flask:</p>
139
<p>Вот некоторые важные вещи, которые стоит помнить при работе с фреймворком Flask:</p>
140
<ul><li>Flask - это микрофреймворк для создания веб-приложений на языке Python.</li>
140
<ul><li>Flask - это микрофреймворк для создания веб-приложений на языке Python.</li>
141
<li>Flask использует декораторы для связывания функций с URL-адресами и методами HTTP.</li>
141
<li>Flask использует декораторы для связывания функций с URL-адресами и методами HTTP.</li>
142
<li>Чтобы удобно отображать HTML-страницы, можно использовать шаблоны, которые упрощают разработку.</li>
142
<li>Чтобы удобно отображать HTML-страницы, можно использовать шаблоны, которые упрощают разработку.</li>
143
<li>Flask не имеет встроенной поддержки баз данных, но к нему всегда можно подключить сторонние - например, SQLite.</li>
143
<li>Flask не имеет встроенной поддержки баз данных, но к нему всегда можно подключить сторонние - например, SQLite.</li>
144
<li>Flask использует объект request для доступа к данным, отправленным пользователем через формы и URL-адреса.</li>
144
<li>Flask использует объект request для доступа к данным, отправленным пользователем через формы и URL-адреса.</li>
145
<li>Flask использует флеш-сообщения для отображения сообщений об ошибках или на веб-странице.</li>
145
<li>Flask использует флеш-сообщения для отображения сообщений об ошибках или на веб-странице.</li>
146
</ul>
146
</ul>