Создание сайта на питоне пошаговая инструкция

Язык Python — это лёгкая дорога в программирование. А Flask помогает проложить путь в веб-разработку и научиться писать сайты с помощью Python. Получается, он такой же лёгкий, как и Python? Да, но есть нюансы.

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

Всё, что нужно знать о Flask в Python:

  • Что это такое
  • Чем он лучше других фреймворков
  • Как установить Flask
  • Как написать простой сайт
  • Как создать блог
  • Что нужно запомнить

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

У Flask много преимуществ, которые выделяют его среди других фреймворков:

  • простой синтаксис — это всё-таки Python;
  • удобные шаблоны — можно быстро создавать прототипы веб-приложений;
  • куча инструментов для гибкой настройки сайтов под любые нужды.

Логотип Flask
Изображение: Wikimedia Commons

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

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

Flask входит в топ-15 самых популярных фреймворков для веб-разработки среди опытных программистов. Рядом с ним в рейтинге находятся Django, Express.js, Laravel, Ruby on Rails, Spring и ASP.NET. И, конечно, на Flask написано немало популярных сайтов. Вот лишь несколько примеров:

  • Pinterest — одна из крупнейших социальных сетей для обмена изображениями и идеями;
  • Netflix — один из крупнейших сервисов видеостриминга в мире;
  • Uber — сервис вызова такси и автомобильного транспорта;
  • Reddit — один из самых популярных новостных UGC-агрегаторов;
  • Twilio — платформа для разработки приложений для обмена сообщениями.

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

У Flask есть ряд особенностей, за которые его любят веб-разработчики. Давайте их перечислим:

  • Минимальный набор инструментов из коробки. Причём они не навязывают какую-то архитектуру или жёсткую структуру проектов. Разработчики сами решают, как и что они будут создавать.
  • Гибкость. Работая с Flask, программист может выбирать только необходимые встроенные инструменты и подключать дополнительные внешние, не перегружая проект лишними модулями.
  • Расширяемость. У Flask много расширений и плагинов, которые помогают быстро добавить новую функциональность. Например, авторизацию, управление базами данных и работу с формами.
  • Простота. У Flask простой синтаксис, что делает изучение этого фреймворка более простым, а также позволяет быстрее создавать прототипы веб-приложений.
  • Поддержка сообщества. Flask запустили в 2010 году, и почти по любому связанному с ним вопросу в интернете уже есть ответы.

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

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

Чтобы установить Flask, сначала нужно установить Python. Приступим.

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

Если вы скачиваете официальную версию Python или пакет Anaconda, у вас автоматически установится PIP. Это менеджер пакетов для Python, который позволяет управлять сторонними библиотеками. Нам он понадобится, чтобы установить Flask.

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

pip --version

или

python3 -m pip --version

В ответ на экран выведется версия PIP. Если ничего не происходит, значит, PIP у вас не установлен. Исправим это:

python -m ensurepip --default-pip

Снова проверим, появился ли в системе менеджер пакетов. Если всё равно что-то не получается, попробуйте найти решение проблемы на Stack Overflow — или обратитесь к астрологу :)

Теперь поставим сам Flask. Делается это очень просто:

pip install Flask

Начнётся процесс загрузки Flask, после которого он будет готов к использованию. Если вам нужна конкретная версия Flask, установить её можно, указав её номер с помощью дополнительного параметра ==<version>.

pip install Flask==<version>

Например, мы можем установить версию 2.0.1:

pip install Flask==2.0.1

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

pip show flask

или создадим Python-файл и впишем туда такую строку:

import flask

Теперь запустим интерпретатор и убедимся, что программа исполняется без ошибок.

Приступим к коду. Для начала нам понадобится основа для приложения. Создадим новый файл с именем app.py. Это и будет наше Flask-приложение.

На первом этапе импортируем класс Flask из библиотеки Flask:

from flask import Flask

Затем создадим экземпляр класса Flask:

app = Flask(__name__)

Здесь мы передаем аргумент __name__ конструктору класса, этот аргумент скажет Flask, где находится наше приложение. Так Flask сможет определить местоположение шаблонов и статических файлов, о которых речь пойдёт дальше. Если вы ещё не особо знакомы с классами в Python, советуем прочитать нашу статью об объектно-ориентированном программировании на Python.

Весь бэкенд строится на маршрутах — или URL-адресах. Они помогают задавать удобную структуру и понятное поведение веб-приложениям.

Для пользователя маршруты — это отдельные «вкладки» на сайте. Например, если зайти на сайт Skillbox, откроется его главная страница www.skillbox.ru. А если кликнуть на любой курс, мы перейдём на другую страницу сайта с другим URL-адресом, таким как www.skillbox.ru/course/profession-python. Видим, что к адресу нашего сайта добавился текст: /course/profession-python/. Эта «приписка» и перенесла нас на другую страницу с другим содержимым. Получается, маршруты позволяют создавать разные страницы с разным наполнением в рамках одного сайта.

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

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

Так мы создали URL-адрес главной страницы сайта. Например, для Skillbox главной страницей будет www.skillbox.ru. Тут мы мысленно можем дописать слеш: www.skillbox.ru/.

Сам маршрут задаётся в строке @app.route(‘/’). Внутрь круглых скобочек мы по ходу статьи будем вписывать разные маршруты, а пока нам хватит стандартного.

Внутрь маршрута мы поместили функцию hello_world(), которая будет выполняться при обращении к корневому URL, или главной странице нашего сайта (ведь наш маршрут ведёт именно на неё). Функция возвращает строку Hello, World! в браузере.

Теперь нам нужно запустить приложение:

if __name__ == '__main__':
    app.run()

Этот код гарантирует, что сервер Flask будет запущен только в том случае, если файл app.py был запущен напрямую, а не импортирован как модуль.

Сохраняем файл app.py и запускаем его с помощью команды в консоли:

python app.py

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

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Чтобы взглянуть на работу нашего приложения, нужно перейти по адресу, который был указан в консоли — http://127.0.0.1:5000/. Вот что мы там увидим:

Скриншот: Skillbox Media

Ликуем — у нас всё получилось, сайт работает. Дальше будем усложнять наше приложение и начнём создавать блог.

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

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

app.py

from flask import Flask

app = Flask(__name__)

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

if __name__ == '__main__':
   app.run()

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

Ещё немного об HTML-шаблонах

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

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

Если вы не знаете, как писать HTML-код, советуем прочитать нашу статью об HTML. А теперь создадим HTML-шаблоны: выделим под них папку templates и добавим в неё файл base.html со следующим содержимым:

<!DOCTYPE html>
<html>
  <head>
    <title>{% block title %}{% endblock %}</title>
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

Этот шаблон будет отправной точкой для всех остальных HTML-страниц. В нём мы прописали заголовок:

<head>
  <title>{% block title %}{% endblock %}</title>
</head>

и основной контент (тело) страницы:

<body>
  {% block content %}{% endblock %}
</body>

Вы, наверное, уже обратили внимание на странные элементы, а точнее теги {% block %} и {% endblock %}. Они как раз нужны, чтобы динамически добавлять туда новые элементы: другие HTML-блоки, JavaScript-код и тому подобное.

Теперь давайте создадим второй шаблон и назовём его index.html. Он будет наследовать элементы базового шаблона:

{% extends "base.html" %}

{% block title %}Home{% endblock %}

{% block content %}
  <h1>Welcome to my website!</h1>
  <p>This is the homepage.</p>
{% endblock %}

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

Пришло время воспользоваться шаблонами. Изменим файл app.py и импортируем функцию render_template из библиотеки Flask, которая позволяет работать с шаблонами:

from flask import Flask, render_template

Изменим маршрут главной страницы и используем в нём новую функцию, чтобы отобразить шаблон index.html:

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

Сохраним изменения в файлах app.py, index.html и base.html, а затем снова запустим наше приложение:

python app.py

Открываем браузер и переходим по адресу http://127.0.0.1:5000/. У нас отобразится содержимое шаблона index.html:

Скриншот: Skillbox Media

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

Мы будем использовать базу данных SQLite, потому что она занимает совсем немного места, легка в освоении и вообще клёвая. А главный плюс — её не нужно скачивать отдельно, потому что она сразу есть в Python.

Займёмся привычным делом — импортируем модуль SQLite3:

import sqlite3

Теперь мы можем создать функцию, которая будет подключать нас к базе данных и возвращать объект подключения:

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

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

Другими словами — наши данные будут храниться в виде таблицы, где каждая строка — уникальный пост, а каждый столбец (или ячейка) — информация об этом посте, например, сам текст, количество лайков, автор и так далее.

Но ещё нам понадобится функция, которая будет закрывать подключение к базе данных:

def close_db_connection(conn):
    conn.close()

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

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

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

Но перед тем как использовать базу данных, её нужно инициализировать и создать таблицу posts.

У нашего учебного поста будет три поля с данными: уникальный идентификатор (ID), заголовок и текст поста. Назовём их соответственно — id, title и content:

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

Функция execute() создаст нам таблицу posts с полями id, title и content. У каждого из этих полей будет собственный тип данных: целое число, строка и строка соответственно.

Мы задали полю id специальный параметр AUTOINCREMENT, чтобы при добавлении нового поста его айдишник автоматически увеличивался на 1. А параметр PRIMARY KEY нужен для уточнения, что строки в таблице уникальные и не пустые.

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

@app.before_first_request
def before_first_request():
    init_db()

Здесь мы используем декоратор @app.before_first_request, который указывает, что функция before_first_request() вызывается перед тем, как запустится рендер шаблонов.

Готово — наше приложение подключено к базе данных SQLite, а мы можем переходить дальше. Но сперва убедимся, что всё работает. Сохраняем изменения и запускаем приложение:

python app.py

Если перейти по адресу http://127.0.0.1:5000/, то может показаться, что ничего не изменилось. Но если посмотреть в папку с файлами, то можно увидеть там новый файл — database.db. Это и есть наша база данных.

Скриншот: Skillbox Media

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

<!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>

В момент рендеринга в эту HTML-страницу мы будем передавать пост, который достали из базы данных. У этого поста будут заголовок (title) и основной текст (content), а всё вместе будет лежать внутри объекта post.

Пока всё просто, но обратите внимание на новую функцию — url_for(). Она позволяет перейти на другой маршрут и отрисовать другую HTML-страницу. В нашем случае, если мы нажмём на ссылку Back to index, запустится функция index(), которая перенаправит нас на главную страницу (маршрут /) и отрисует шаблон index.html.

Теперь нам нужно сделать новый маршрут под один из постов в файле app.py:

@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(‘/<int:post_id>’) — задаём новый маршрут. Он будет выглядеть так: www.oursite.com/1. Единица будет указывать на индекс поста из базы данных. <int:post_id> — это ещё одно указание на то, что индекс поста должен быть целым числом (int), а не, например, строкой.
  • get_post(post_id) — передаём айдишник поста, который как раз и «встанет» в URL-адрес и добавится к запросу к базе данных.
  • post = conn.execute (‘SELECT * FROM posts WHERE id =? ‘, (post_id,)).fetchone() — запрашиваем из базы данных пост по нашему айдишнику и берём одну строку функцией fetchone().
  • return render_template (‘post.html’, post=post) — рендерим HTML-шаблон и передаём туда полученный пост.

Давайте убедимся, что всё работает корректно и страница отрисовывается. Временно напишем «костыль» и вручную добавим пост (исключительно для проверки):

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

Наш «костыль» — это четвёртая строка. Здесь мы сами вставляем новую строку в базу данных с помощью запроса INSERT. Теперь перейдём по адресу http://127.0.0.1:5000/1:

Скриншот: Skillbox Media

Круто — всё отрисовалось, кнопка «Назад» работает! Идём дальше и не забываем удалить «костыль».

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

<!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>

Главный движ у нас происходит в теге <ul>: мы идём по всем элементам списка posts и для каждого создаём заголовок со ссылкой на пост. При нажатии на ссылку вызывается функция url_for(), а ей передаётся функция-рендер get_post() и ID поста.

Ещё мы немного декорировали заголовок самой страницы. Можете сравнить с прошлой версией.

Если перезапустить приложение, то мы не увидим новых постов, потому что их просто нет в базе данных. Но можно опять заполнить всё вручную. Изменим функцию index():

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

Видим результат:

Скриншот: Skillbox Media

Правда, если перейти по ссылке, то ничего не отобразится, но и это мы со временем исправим.

Отображение постов работает, осталось добавить возможность создавать новые посты. Для этого создадим новый HTML-шаблон и метод для рендеринга.

В папке templates создаём новый файл add_post.html:

{% 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 %}

Он будет наследовать файл base.html, а внутри содержать форму, где пользователь указывает заголовок и тело поста. Если он вдруг введёт некорректные данные, то мы покажем ему сообщение об ошибке с помощью флеш-сообщений (когда некорректно заполненное поле подсвечивается красным и у вас нет возможности отправить заполненную форму).

Теперь добавим новый маршрут в файл app.py:

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

Этот маршрут обрабатывает GET- и POST-запросы по адресу /new. Если запрос выполняется методом GET, то функция просто отображает форму для ввода данных с помощью шаблона add_post.html. А если запрос выполняется методом POST, мы достаём заголовок и тело поста из формы, а затем добавляем их в базу данных. В конце — редиректим (то есть перенаправляем) пользователя обратно на главную.

Подробнее об HTTP-запросах вы можете прочитать в нашей статье.

Чтобы все эти функции работали, мы также должны их импортировать в начале файла:

from flask import Flask, render_template, request, redirect, url_for

Теперь давайте сохраним свои наработки и запустим приложение:

python app.py

Переходим по адресу http://127.0.0.1:5000/ и видим, что опять ничего не изменилось:

Скриншот: Skillbox Media

Попробуем перейти по адресу http://127.0.0.1:5000/new:

Скриншот: Skillbox Media

Ура! Мы видим нашу форму! Давайте впишем туда что-нибудь и нажмём Submit:

Скриншот: Skillbox Media

Нас перекинуло на главную страницу — и на ней находится наш новый пост. Ура! Всё наконец-то работает!

Скриншот: Skillbox Media

К тому же теперь мы можем перейти по ссылке и посмотреть пост целиком:

Скриншот: Skillbox Media

Но всё ещё есть проблема — нужно добавить кнопку Add new post, чтобы каждый раз не добавлять пост, вбивая в адресную строку наш URL. Для этого нам нужно добавить всего одну строчку в файл index.html:

<!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>

Мы создали кнопку, нажав которую, мы перейдём на страницу со ссылкой для создания нового поста. Проверяем:

Скриншот: Skillbox Media

Кликаем — переходим куда нужно:

Скриншот: Skillbox Media

Добавили новый пост:

Скриншот: Skillbox Media

Наше приложение приобрело следующую структуру:

Blog Flask/
|-- templates/
|   |-- index.html
|   |-- base.html
|   |-- post.html
|   |-- add_post.html
|-- app.py
|-- database.db

В папке templates хранятся HTML-шаблоны: index.html, add_post.html, post.html и base.html. Файл app.py содержит основной код приложения Flask, в котором определены маршруты и функции для работы с базой данных. А файл database.db — это база данных SQLite.

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

index.html:

<!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>

base.html:

<!DOCTYPE html>
<html>
  <head>
    <title>{% block title %}{% endblock %}</title>
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

post.html:

<!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>

add_post.html:

{% 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 %}

app.py:

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

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

Вот некоторые важные вещи, которые стоит помнить при работе с фреймворком Flask:

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

Flask – компактный фреймворк для быстрой разработки веб-приложений. Он предоставляет минимальную необходимую функциональность и не навязывает никаких строгих правил в отношении структуры и архитектуры приложения (как это делает Django).

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

Основные преимущества Flask:

  • Минималистичность. Flask отличается небольшим размером – в нем есть все самое необходимое и нет ничего лишнего.
  • Гибкость. Фреймворк не диктует определенных правил и позволяет разработчику сохранить полный контроль над структурой приложения.
  • Простота в использовании. Он имеет несколько встроенных функций, которые позволяют сразу начать создавать полноценные веб-приложения, даже если у вас нет опыта в веб-разработке на Python. Например, у Flask есть встроенный сервер, поддержка сессий, обработчик форм, шаблонизатор.
  • Интеграция с дополнительными библиотеками. Фреймворк очень просто интегрируется с многочисленными библиотеками, которые расширяют его функциональность. Это позволяет создать гибкий, масштабируемый проект для любой сферы.
  • Простота тестирования. У Flask есть встроенный тестовый клиент, который максимально упрощает тестирование и отладку.

Установка

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

        python -m venv fprojectvenv
    

Перейдите в только что созданную директорию:

        cd fproject
    

Активируйте окружение:

        venvscriptsactivate
    

И установите Flask:

        pip install flask
    

Активировать виртуальное окружение нужно перед каждым
сеансом работы с Flask.

Напишем приложение, которое будет выводить традиционное
приветствие Hello, World! в браузере. Сохраните
этот код в файле app.py в
директории fproject:

        from flask import Flask

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run()

    

Этот код создает объект приложения Flask с помощью класса Flask и присваивает его переменной app. Декоратор @app.route('/') устанавливает маршрут для
главной страницы нашего приложения, а метод def hello() определяет, что будет отображаться на этой странице.

if
__name__ == '__main__':
проверяет,
запускается ли данный файл как самостоятельное приложение, или импортируется как модуль. В
нашем случае он запускается как независимое приложение, поэтому вызывается метод app.run(), который запускает веб-сервер Flask.

Запустите приложение в командой строке:

        (venv) C:UsersUserfproject>app.py

    

Откройте адрес http://localhost:5000/
в браузере:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Flask по умолчанию использует порт 5000. При желании его можно
изменить на более привычный 8000:

        app.run(port=8000)
    

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

        app.run(debug=True)
    

Для остановки сервера нажмите Ctrl+C.

Маршруты в Flask

Маршруты – это URL-адреса, по которым пользователи могут открывать определенные
страницы (разделы) веб-приложения. Маршруты в Flask определяются с помощью декоратора
@app.route(). Для каждого маршрута можно
написать отдельную функцию представления, которая будет выполнять какие-то действия при переходе по определенному адресу. Рассмотрим пример:

        from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return 'Это главная страница.'

@app.route('/about')
def about():
    return 'Здесь будет информация об авторе сайта.'

@app.route('/blog')
def blog():
    return 'Это блог с заметками о работе и увлечениях.'

if __name__ == '__main__':
    app.run()

    

Сохраните код, запустите приложение, последовательно
откройте адреса:

  • http://localhost:5000/
  • http://localhost:5000/about
  • http://localhost:5000/blog

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Переменные в маршрутах

В URL можно передавать различные значения. Запустите этот
код и перейдите по адресу, например, http://localhost:5000/user/EvilAdmin

        from flask import Flask

app = Flask(__name__)

@app.route('/user/<username>')
def user_profile(username):
    return f"Это профиль пользователя {username}"

if __name__ == '__main__':
    app.run()

    

Имя пользователя, переданное в качестве переменной, будет
показано на странице:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

А так можно передать в маршруте целое число:

        from flask import Flask

app = Flask(__name__)

@app.route('/user/<int:user_id>')
def user_profile(user_id):
    return f"Это профиль пользователя с ID {user_id}"

if __name__ == '__main__':
    app.run()

    

Перейдите по адресу, например, http://localhost:5000/user/5:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

GET- и POST-запросы

GET и POST – это HTTP-запросы, которые используются для
отправки данных между клиентом и сервером.

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

POST-запрос используют для отправки данных на сервер. При
выполнении POST-запроса клиент отправляет данные на сервер, а сервер их обрабатывает. POST-запросы обычно применяют для отправки форм, с данными из которых нужно что-то сделать на бэкенде.

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

        from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        # проверка логина и пароля
        return 'Вы вошли в систему!'
    else:
        return render_template('login.html')

if __name__ == '__main__':
    app.run()

    

Маршрут @app.route('/login', methods=['GET', 'POST']) обрабатывает и POST, и GET-запросы: в первом случае он отправит
данные на сервер, во втором – просто выведет страницу с формой авторизации.

Для вывода формы на странице сделаем простейший шаблон. Этот
код нужно сохранить в файле login.html, в директории templates (в этой папке Flask по
умолчанию ищет шаблоны):

        {% extends 'base.html' %}

{% block content %}
  <div class="container">
    <div class="row justify-content-center mt-5">
      <div class="col-md-6">
        <div class="card">
          <div class="card-header">
            <h1 class="text-center">Вход на сайт</h1>
          </div>
          <div class="card-body">
            {% with messages = get_flashed_messages() %}
              {% if messages %}
                <div class="alert alert-danger">
                  <ul>
                    {% for message in messages %}
                      <li>{{ message }}</li>
                    {% endfor %}
                  </ul>
                </div>
              {% endif %}
            {% endwith %}
            <form method="post">
              <div class="mb-3">
                <label for="username" class="form-label">Логин:</label>
                <input type="text" class="form-control" id="username" name="username" required>
              </div>
              <div class="mb-3">
                <label for="password" class="form-label">Пароль:</label>
                <input type="password" class="form-control" id="password" name="password" required>
              </div>
              <div class="text-center">
                <button type="submit" class="btn btn-primary">Войти</button>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
{% endblock %}
    

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

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Шаблонизатор Jinja2

Шаблоны в Flask
используются для динамического формирования веб-страниц. Шаблоны представляют
собой HTML страницы, в
которые можно передавать любые данные с бэкенда. К шаблонам можно подключать
любые CSS-фреймворки
типа Bootstrap и Tailwind,
и любые JS-скрипты.

Поведением шаблонов управляет шаблонизатор Jinja2 – он предоставляет
функциональность для создания условий, циклов, макросов, наследования и блоков.
Главные преимущества шаблонизатора:

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

Наследование работает так:

  • Базовый шаблон, который обычно называется base.html, содержит общую разметку для сайта.
  • В base.html подключаются локальные и CDN-фреймворки (CSS, JS), задаются фоновые изображения и фавикон.
  • Дочерние шаблоны наследуют этот базовый шаблон и дополняют его своим собственным контентом.

Продемонстрируем наследование на примере. Сохраните в папке templates два файла. Это
содержимое файла base.html – в нем
подключается CSS-фреймворк
Bootstrap, кастомные
стили custom.css из
статической папки static,
иконки Font Awesome:

        <!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{% block title %}{% endblock %}</title>
    <!-- Bootstrap стили -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- иконки fontawesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <!-- кастомные стили -->
    <link rel="stylesheet" href="{{ url_for('static', filename='custom.css') }}">
</head>
<body>

<!-- навигация -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
        <a class="navbar-brand" href="#">Мой личный сайт</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                data-bs-target="#navbarNav" aria-controls="navbarNav"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse justify-content-end"
             id="navbarNav">
            <ul class="navbar-nav">
                <li class="nav-item">
                    <a class="nav-link" href="#">Главная</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Обо мне</a>
                </li>
            </ul>
        </div>
    </div>
</nav>

<!-- контент дочерних шаблонов -->
<div class="container my-3">
    {% block content %}
    {% endblock %}
</div>

</body>
</html>

    

Шаблон base.html также содержит верхнее меню для навигации по сайту – это меню будут наследовать все дочерние шаблоны. Вот пример дочернего шаблона about.html:

        {% extends 'base.html' %}

{% block title %}Мое резюме{% endblock %}

{% block content %}
    <div class="container my-5">
        <h1 class="text-center mb-4">Мое резюме</h1>
        <div class="row">
            <div class="col-md-6">
                <h2>Образование</h2>
                <h4>Московский политехнический институт</h4>
                <p class="mb-0">Бакалавр Computer Science</p>
                <p class="text-muted">2016 - 2020</p>
            </div>
            <div class="col-md-6">
                <h2>Опыт работы</h2>
                <h4>Web Developer - XYZ компания</h4>
                <p class="mb-0">2019 - н.в.</p>
                <p class="text-muted">- Разработка и поддержка веб-приложений</p>
                <p class="text-muted">- Работа с Python, Django, HTML/CSS, JavaScript, MySQL</p>
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-md-6">
                <h2>Навыки</h2>
                <ul class="list-group">
                    <li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> Python</li>
                    <li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> Django</li>
                    <li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> HTML/CSS</li>
                    <li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> JavaScript</li>
                </ul>
            </div>
            <div class="col-md-6">
                <h2>Проекты</h2>
                <h4>Сайт для продажи автомобилей</h4>
                <p class="text-muted mb-0">- Разработка сайта с использованием Django</p>
                <p class="text-muted">- Интеграция с API маркетплейса для получения данных об автомобилях</p>
                <h4>Игровой блог</h4>
                <p class="text-muted mb-0">- Разработка блога с использованием Django</p>
                <p class="text-muted">- Возможность создавать учeтные записи пользователей и писать комментарии</p>
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-md-6">
                <h2>Контакты</h2>
                <p class="mb-0"><i class="fa fa-phone"></i> Телефон: +990123456789</p>
                <p class="mb-0"><i class="fa fa-envelope"></i> Email: example@example.com</p>
                <p class="mb-0"><i class="fa fa-github"> GitHub: <a href="https://github.com/example"></i>example</a></p>
            </div>
            <div class="col-md-6">
                <h2>Языки</h2>
                <ul class="list-group">
                    <li class="list-group-item border-0 py-1"><i class="fa fa-check-circle"></i> Английский (C1)</li>
                    <li class="list-group-item border-0 py-1"><i class="fa fa-check-circle"></i> Немецкий (B2)</li>
                    <li class="list-group-item border-0 py-1"><i class="fa fa-check-circle"></i> Русский (родной)</li>
                </ul>
            </div>
        </div>
    </div>
{% endblock %}

    

Фон страницы шаблонизатор берет из файла static/customs.css:

        body {
    background-color: #e5e5e5;
}

    

А код для вывода страницы выглядит так:

        from flask import Flask, render_template

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run(debug=True)

    

Запустите приложение, откройте адрес http://localhost:5000/about:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Работа с базой данных

Для работы с базами данных в Flask удобно использовать ORM SQLAlchemy.
Как уже упоминалось в предыдущей главе о SQLite, ORM играет роль своеобразной прослойки между приложением и СУБД SQLite, и позволяет работать
с базами без использования языка SQL. Надо заметить, что работать с базами данных в SQLAlchemy
немного сложнее, чем в Django ORM,
но гораздо проще, чем в чистом Python.

Начнем с установки SQLAlchemy в виртуальное окружение:

        pip install flask-sqlalchemy
    

В SQLAlchemy основой для создания таблиц в базе данных
служат модели (специальные классы). Поля классов определяют структуру таблицы, которая будет
использоваться для хранения информации в базе данных. В полях классов можно
задавать типы данных, которые соответствуют типам данных в БД,
например, String для хранения строк, Integer для целых чисел, Float для
плавающих чисел и т.д.

SQLAlchemy, как и другие ORM, очень упрощает создание связей между таблицами. В
приведенном ниже примере используется связь один ко многим (ForeignKey),
поскольку у одного исполнителя может быть несколько альбомов, а в одном альбоме
всегда будет несколько треков:

        from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Artist(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)

class Album(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    year = db.Column(db.String(4), nullable=False)
    artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'), nullable=False)
    artist = db.relationship('Artist', backref=db.backref('albums', lazy=True))

class Song(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    length = db.Column(db.String(4), nullable=False)
    track_number = db.Column(db.Integer, nullable=False)
    album_id = db.Column(db.Integer, db.ForeignKey('album.id'), nullable=False)
    album = db.relationship('Album', backref=db.backref('songs', lazy=True))

    

Сохраните этот код в файле models.py – мы будем импортировать модели из него в главный файл приложения app.py и в скрипт create_db.py, который создает базу данных и заполняет ее тестовой информацией.

Код для create_db.py будет следующим:

        from flask import Flask
from models import Artist, Album, Song, db
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///music.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)


if __name__ == '__main__':
    with app.app_context():
        db.create_all()

        # создаем тестовых исполнителей
        artist1 = Artist(name='The Rolling Stones')
        artist2 = Artist(name='Jefferson Airplane')
        artist3 = Artist(name='Nine Inch Nails')
        artist4 = Artist(name='Tool')
        db.session.add_all([artist1, artist2, artist3, artist4])
        db.session.commit()

        # создаем тестовые альбомы
        album1 = Album(title='Aftermath', year='1966', artist=artist1)
        album2 = Album(title='Beggars Banquet', year='1968', artist=artist1)
        album3 = Album(title='Surrealistic Pillow', year='1967', artist=artist2)
        album4 = Album(title='Broken', year='1992', artist=artist3)
        album5 = Album(title='The Fragile', year='1999', artist=artist3)
        album6 = Album(title='Lateralus', year='2001', artist=artist4) 
        album7 = Album(title='AEnima', year='1996', artist=artist4) 
        album8 = Album(title='10,000 Days', year='2006', artist=artist4) 

        # создаем тестовые песни
        song1 = Song(title='Paint it Black', length='4:20', track_number=1, album=album1)
        song2 = Song(title='Sympathy For The Devil', length='3:53', track_number=2, album=album1)
        song3 = Song(title='White Rabbit', length='3:42', track_number=5, album=album3)
        song4 = Song(title='Wish', length='3:46', track_number=6, album=album4)
        song5 = Song(title='Starfuckers, Inc.', length='5:00', track_number=1, album=album5)
        song6 = Song(title='Schism', length='6:46', track_number=7, album=album6)
        song7 = Song(title='Eulogy', length='8:29', track_number=3, album=album7)
        song8 = Song(title='Vicarious', length='7:07', track_number=5, album=album8)
        db.session.add_all([album1, album2, album3, album4, album5, album6, album7, album8, song1, song2, song3, song4, song5, song6, song7, song8])
        db.session.commit()
    

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

        (venv) C:UsersUserfproject>create_db.py
    

В реальных приложениях базу данных удобнее заполнять,
например, с помощью модуля csv – мы рассмотрим этот метод ниже, в одном из заданий.

Главный файл приложения app.py
выглядит так:

        from flask import Flask, render_template
from models import Artist, Album, Song, db

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///music.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# связываем приложение и экземпляр SQLAlchemy
db.init_app(app)


@app.route('/songs')
def songs():
    songs_list = Song.query.all()
    return render_template('songs.html', songs=songs_list)

if __name__ == '__main__':
    app.run(debug=True)
    

Шаблон songs.html использует
тот же самый базовый base.html, что и предыдущий
пример:

        {% extends 'base.html' %}

{% block title %}
Мои любимые песни
{% endblock %}

{% block content %}
<h1 class="mb-5">Мои любимые песни</h1>

<div class="card-columns">
  <div class="row">
  {% for song in songs %}
    <div class="col-md-3">
      <div class="card mb-3">
          <div class="card-header fw-bold">{{ song.title }}</div>
          <div class="card-body">
              <p class="badge bg-primary text-wrap">{{ song.album.artist.name }}</p>
              <p class="card-text">Альбом: 
              <strong>{{ song.album.title }}</strong></p>
              <p class="card-text">Длина: {{ song.length }} минут</p>
              <p class="card-text">Номер трека: {{ song.track_number }}</p>
              <p class="card-text">Дата релиза: {{ song.album.year }}</p>
          </div>
      </div>
    </div>
  {% endfor %}
  </div>
</div>

{% endblock %}

    

После создания шаблона можно запустить приложение:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Практика

Задание 1

Напишите Flask-приложение,
которое выводит в шаблон index.html приветствие для
пользователя. Приветствие зависит от времени суток:

  • С 6:00 до 12:00 – «Доброе утро»
  • С 12:00 до 18:00 – «Добрый день»
  • С 18:00 до 24:00 – «Добрый вечер»
  • С 00:00 до 6:00 – «Доброй ночи»

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Код для app.py выглядит
так:

        from flask import Flask, render_template
import datetime

app = Flask(__name__)

@app.route('/')
def index():
    now = datetime.datetime.now()
    if now.hour >= 6 and now.hour < 12:
        greeting = 'Доброе утро'
    elif now.hour >= 12 and now.hour < 18:
        greeting = 'Добрый день'
    elif now.hour >= 18 and now.hour < 24:
        greeting = 'Добрый вечер'
    else:
        greeting = 'Доброй ночи'
    return render_template('index.html', greeting=greeting)

if __name__ == '__main__':
    app.run(debug=True)

    

Для вывода приветствия используются шаблоны base.html и
index.html.

Задание 2

Напишите Flask-приложение, которое с помощью шаблона выводит пронумерованный список дел.

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Это код приложения app.py:

        from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def task_list():
    tasks = ["Выгулять собаку", "Погладить рубашку", "Зайти в супермаркет", 
    "Убрать на кухне", "Дописать статью", "Позвонить тимлиду"]
    return render_template('task_list.html', tasks=tasks)

if __name__ == '__main__':
    app.run()

    

Нумерацию в Jinja2
легко реализовать с помощью loop.index:

        <!DOCTYPE html>
<html>
  <head>
    <title>Мой список важных дел</title>
    <style>
      ul.no-bullets {
      list-style-type: none; 
      }
    </style>
  </head>
  <body>
    <h1>Список дел на сегодня:</h1>
    <ul class="no-bullets">
      {% for task in tasks %}
        <li>{{loop.index}}. {{ task }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

    

Задание 3

Напишите app.py и
шаблон welcome.html, которые выводят
различный контент для пользователей с разными правами доступа:

  • Админ имеет полный доступ.
  • Модератор может редактировать записи и комментарии.
  • Рядовой пользователь может создавать записи от своего имени и просматривать френдленту.

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

В приложении app.py можно
определить только маршрут, вся функциональность по определению уровней доступа
находится в шаблоне:

        <!DOCTYPE html>
<html>
  <head>
    <title>Личный кабинет</title>
  </head>
  <body>
    {% if user_level == 'admin' %}
      <h1>Привет, админ!</h1>
      <p>У тебя есть доступ ко всем настройкам.</p>
      <a href="#">Редактирование профилей</a>
      <a href="#">Создание учетных записей</a>
      <a href="#">Публикация статей</a>
    {% elif user_level == 'moderator' %}
      <h1>Привет, модератор!</h1>
      <p>У тебе есть доступ к редактированию записей.</p>
      <a href="#">Редактирование записей</a>
      <a href="#">Модерация комментариев</a>
    {% else %}
      <h1>Привет, пользователь!</h1>
      <p>У тебя нет доступа к редактированию контента и настроек.</p>
      <a href="#">Новая запись</a>
      <a href="#">Записи друзей</a>      
    {% endif %}
  </body>
</html>

    

Задание 4

Напишите скрипт для создания и заполнения базы данных SQLite данными о книгах из
файла books.json, а также app.py и
шаблоны, которые выводят:

  • Карточки с информацией о книгах.
  • Карточку отдельной книги.

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Напишем модель
Book и скрипт,
который создает и заполняет базу из json-файла. Затем создадим app.py с
двумя маршрутами – для вывода всех
книг, и для вывода отдельной
книги:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Задание 5

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

  • Каждый производитель (Manufacturer) поставляет несколько типов товаров (Category) – ноутбуки, наушники, смартфоны и так далее.
  • Одну и ту же категорию товаров могут производить несколько компаний.
  • В каждой категории может быть множество товаров (Item).

Нужно реализовать вывод всех товаров по поставщикам и по
категориям. Все данные о товарах находятся в файле info.csv.

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Файл models.py, описывающий структуру
базы данных, выглядит так:

        from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

manufacturer_items = db.Table('manufacturer_items',
    db.Column('manufacturer_id', db.Integer, db.ForeignKey('manufacturer.id'), primary_key=True),
    db.Column('item_id', db.Integer, db.ForeignKey('item.id'), primary_key=True)
)

class Manufacturer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    items = db.relationship('Item', secondary=manufacturer_items, backref=db.backref('manufacturers', lazy=True))

    def __repr__(self):
        return '<Manufacturer %r>' % self.name

class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    items = db.relationship('Item', backref='category')

    def __repr__(self):
        return '<Category %r>' % self.name

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.String(255), nullable=False)
    price = db.Column(db.Float, nullable=False)
    quantity = db.Column(db.Integer, nullable=False, default=0)
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)

    def __repr__(self):
        return '<Item %r>' % self.

    

Для заполнения базы данными из файла info.csv напишем скрипт.
Приложение использует 4 шаблона:

  • base.html
  • index.html
  • categories.html
  • items.html

За вывод всех товаров определенного производителя в
отдельной категории отвечает эта функция:

        @app.route('/<manufacturer>/<category>')
def show_items(manufacturer, category):
    items = Item.query.join(Item.manufacturers).join(Item.category).
                filter(Manufacturer.name == manufacturer).
                filter(Category.name == category).all()
    return render_template('items.html', manufacturer=manufacturer, category=category, items=items)

    

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Задание 6

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

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Приложение состоит из файлов app.py, create_db.py и models.py.
Для добавления новых товаров используется шаблон add.html и
маршрут/функция add:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Задание 7

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

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Начнем с создания базы данных на основе моделей.
Функции add_patient,
edit_patient, delete_patient
реализованы в приложении app.py.
Шаблоны add.html и
edit.html обеспечивают
добавление и редактирование карточек:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Задание 8

Напишите модуль аутентификации для Flask-приложения. Необходимо
реализовать:

  • регистрацию;
  • проверку учетных данных при входе;
  • перенаправление на страницу профиля.

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Проект включает в себя файлы app.py, create_db.py и models.py. Кроме того, модуль
использует шаблоны:

  • base.html
  • index.html
  • login.html
  • profile.html
  • register.html

После регистрации и входа пользователь перенаправляется на страницу
своего профиля:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Задание 9

Напишите веб-приложение для хранения заметок. Необходимо
реализовать набор CRUD
операций – вывод, добавление, редактирование и удаление заметок. При создании
базы данных следует учесть, что заметка может относиться только к одной
категории, а в категории может быть множество заметок. На главной странице
выводятся все заметки, причем текст ограничивается первыми 300 символами.
Нажатие на ссылку «Далее» открывает страницу с полным текстом заметки.

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

База данных для приложения создается с помощью скрипта create_db.py на основе моделей.
CRUD операции
описаны в app.py. При нажатии на
название категории шаблон и маршрут category выводят все заметки, относящиеся
к данной теме.

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Задание 10

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

Пример:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

Решение:

Прежде всего разработаем
модели Tag, Category и
Post, а затем создадим
на их основе базу данных при помощи скрипта.

Приложение использует следующие шаблоны:

  • base.html
  • category.html
  • delete_post.html
  • edit_post.html
  • index.html
  • new_post.html
  • post.html
  • tag.html

CRUD операции реализованы в главном файле приложения app.py. Чтобы самые свежие
записи выводились первыми, в models.py мы
определили метод newest_first. При нажатии на ссылку «Читать»
выводится полный текст записи:

🐍 Самоучитель по Python для начинающих. Часть 23: Основы веб-разработки на Flask

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

Мы рассмотрели основные приемы разработки простых
веб-приложений на основе фреймворка Flask:

  • Создание маршрутов и функций представления.
  • Получение и обработку данных с фронтенда.
  • CRUD операции.
  • Основные возможности шаблонизатора Jinja2.

При создании приложений Flask для получения данных с фронтенда обычно используются формы WTF Forms.
В этой статье при решении заданий намеренно использовались HTML-формы – так процесс передачи и
обработки данных выглядит максимально понятно.

В следующей статье будем изучать NumPy.

Содержание самоучителя

  1. Особенности, сферы применения, установка, онлайн IDE
  2. Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
  3. Типы данных: преобразование и базовые операции
  4. Методы работы со строками
  5. Методы работы со списками и списковыми включениями
  6. Методы работы со словарями и генераторами словарей
  7. Методы работы с кортежами
  8. Методы работы со множествами
  9. Особенности цикла for
  10. Условный цикл while
  11. Функции с позиционными и именованными аргументами
  12. Анонимные функции
  13. Рекурсивные функции
  14. Функции высшего порядка, замыкания и декораторы
  15. Методы работы с файлами и файловой системой
  16. Регулярные выражения
  17. Основы скрапинга и парсинга
  18. Основы ООП: инкапсуляция и наследование
  19. Основы ООП: абстракция и полиморфизм
  20. Графический интерфейс на Tkinter
  21. Основы разработки игр на Pygame
  22. Основы работы с SQLite
  23. Основы веб-разработки на Flask

***

Материалы по теме

  • 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages
  • 🐍📚 Создаем аналог LiveLib.ru на Flask. Часть 1: основы работы с SQLAlchemy
  • 🐍⛩️🥤 Руководство для начинающих по шаблонам Jinja в Flask

На чтение 10 мин Просмотров 22.1к. Опубликовано 18.11.2021

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

Для начала устанавливаем колбу:

Содержание

  1. Шаг 1: минимальное веб-приложение
  2. Шаг 2: Добавление HTML
  3. Шаг 3: Добавление CSS
  4. Шаг 4: добавление изображения
  5. Шаг № 5: Добавление JavaScript

Шаг 1: минимальное веб-приложение

Минимальное приложение можно найти по адресу https://flask.palletsprojects.com/en/2.0.x/quickstart/#a-minimal-application. Это веб-страница, на которой отображается «Hello World». Первым делом мы создали экземпляр Flask () с аргументом «__name__». Декоратор маршрута используется для того, чтобы сообщить Flask URL-адрес, который активирует написанную нами функцию.

from flask import Flask

app = Flask(__name__)

@app.route(‘/’)

def index():
return «Hello World»

if «__name__» == «__main__»:
app.run(debug=True)

Затем в терминале PyCharm введите следующее (где имя моего файла Python — main.py; в вашем случае замените main.py именем вашего файла Python):

set FLASK_APP=main.py
$env:FLASK_APP = «main.py»
flask run

Как только вы запустите «flask run», терминал выдаст URL-адрес с портом. Этот URL: ПОРТ — это место, где загружается веб-страница. Вы всегда можете нажать Control + c, чтобы выйти. В моем случае написано: «Запуск на http://127.0.0.1:5000/ (нажмите CTRL + C, чтобы выйти)». Итак, откройте свой веб-браузер, скопируйте и вставьте указанный URL. В моем случае я скопировал и вставил «http://127.0.0.1:5000/». Также обратите внимание, что предыдущие строки должны запускаться каждый раз, когда вы перезапускаете PyCharm, чтобы он работал:

Как только вы запустите «flask run», терминал выдаст URL-адрес с портом

Шаг 2: Добавление HTML

Первое, что вам нужно сделать, это открыть папку, в которой находится скрипт Python, и создать папку под названием «шаблоны». Когда я впервые запустил это, я попытался указать имя «шаблон» в качестве имени папки, и вся программа вылетела и не работала. Поэтому обязательно назовите папку « Шаблоны ». В этой папке «шаблонов» создайте файл index.html с вашим HTML-кодом. Затем используйте render_template () и передайте index.html в качестве аргумента. Теперь, если вы запустите «flask run» в терминале, ваш HTML-код должен отобразиться:

Мой html-код (index.html) на данный момент выглядит следующим образом:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
</head>
<body>
<h1>
Kalyani’
s Resume
This page will contain my resume
</h1>
</body>
</html>

И мой код файла Python (main.py) выглядит следующим образом:

from flask import Flask, render_template

app = Flask(__name__)

@app.route(‘/’)

def index():
return render_template(«index.html»)

if «__name__» == «__main__»:
app.run(debug=True)

Последний будет отображать простую HTML-страницу.

Шаг 3: Добавление CSS

Теперь я хочу добавить CSS в свой HTML. Для этого создайте папку с именем «static» и создайте файл с именем «main.css». Здесь имя фактического файла CSS может быть любым. Я решил назвать свой «main.css». Однако имя папки должно быть «статическим»! Фактически, в «статической» папке можно разместить все, что является статическим, например CSS, JavaScript и изображения. Итак, если вы собираетесь размещать изображения, JavaScript и CSS, вы можете создать подпапки.

Теперь я хочу добавить CSS в свой HTML

Сначала напишем нужный мне CSS (main.css):

body {
margin:0;
color: #333
font-family: verdana;
font-size: 20px;
background-color: rgb(201, 76, 76);
}
.styled {
background-color: #92a8d1;
font-family: verdana;
font-size: 20px;
}

Здесь, в index.html, нам нужно написать & lt; link rel = «stylesheet» type = «text / css» href = «{{url_for (‘static’, filename = ‘main.css’)}}» > в заголовке HTML-файла. Здесь имя файла — это имя файла CSS (у меня main.css). Если, например, «main.css» находится с подпапкой «css», вы должны написать следующее:

 <link rel=«stylesheet» type=«text/css» href=«{{ url_for(‘static’, filename=’css/main.css’)}}»>.

После этого вы можете использовать созданный вами CSS. Например, я создал один под названием «стилизованный» и использовал его в классе h1.

Мой файл index.html будет следующим:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
<link rel=»stylesheet» type=»text/css» href=»{{ url_for(‘
static‘, filename=’main.css‘)}}»>
</head>
<body>

<h1 class=»styled»>
Kalyani’s Resume
This page will contain my resume
</h1>
</body>
</html>

Главный файл Python — main.py — остается прежним.

from flask import Flask, render_template

app = Flask(__name__)

@app.route(‘/’)

def index():
return render_template(«index.html»)

if «__name__» == «__main__»:
app.run(debug=True)

Шаг 4: добавление изображения

Теперь давайте добавим изображение на созданную HTML-страницу! Для этого мы используем созданную нами «статическую» папку. Внутри «статической» папки я создал другую папку под названием «изображения». В папке изображений я поместил изображение. Теперь давайте добавим изображение в HTML-код следующим образом: <img src = ”/ static / images / kalyani.jpg” height = ”200 ″ />. В этом случае я установил высоту изображения на 200, но вы можете изменить ее на все, что захотите, и добавить CSS, если хотите.

Теперь давайте добавим изображение на созданную HTML-страницу

HTML-код будет выглядеть следующим образом:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
<link rel=»stylesheet» type=»text/css» href=»{{ url_for(‘
static‘, filename=’main.css‘)}}»>
</head>
<body>
<img src=»/static/images/kalyani.jpg» height=»200″ />
<h1 class=»styled»>
Kalyani’
s Resume
</h1>
This page will contain my resume
</body>
</html>

В качестве альтернативы можно также использовать следующее:

<img src=«{{ url_for(‘static’, filename=’images/kalyani.jpg’)}}» height=«200» />.

В этом случае HTML-код будет выглядеть так:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
<link rel=»stylesheet» type=»text/css» href=»{{ url_for(‘
static‘, filename=’main.css‘)}}»>
</head>
<body>
<img src=»{{ url_for(‘
static‘, filename=’images/kalyani.jpg‘)}}» height=»200″ />
<h1 class=»styled»>
Kalyani’
s Resume
</h1>
This page will contain my resume
</body>
</html>

Шаг № 5: Добавление JavaScript

Есть два способа добавить JavaScript. В этой первой демонстрации я создам кнопку. Когда кнопка нажата, она активирует функцию myFunction (), которая будет JavaScript (находится в теге <script>). Для этого настройте кнопку. Затем установите тег скрипта в заголовке HTML-кода и определите в нем функцию. В моем случае я определил функцию, которая будет добавлять «все резюме» к элементу ap при нажатии кнопки.

Вы можете добавить его в файл index.html следующим образом:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
<link rel=»stylesheet» type=»text/css» href=»{{ url_for(‘
static‘, filename=’main.css‘)}}»>

function myFunction() {
document.getElementById(«para»).innerHTML = «WHOLE RESUME»;
}
</script>
</head>
<body>
<img src=»/static/images/kalyani.jpg» height=»200″ />
<h1 class=»styled»>
Kalyani’s Resume
</h1>
This page will contain my resume
<br>
<br>
<id=«para»> </p>
<<button id=«button» type=«button» onclick=«myFunction()»> Click to see Resume </button>
</body>
</html>

Однако в большинстве случаев файлы JavaScript, как правило, сами по себе являются документами, а не однострочниками. В таких случаях у нас будет файл.js, который нужно связать. В моем случае я бы написал: <script src = ”/ static / javascript / javascript.js”> </script>. Итак, как и в случае с файлом изображения, мы связываем файл js следующим образом:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
<link rel=»stylesheet» type=»text/css» href=»{{ url_for(‘
static‘, filename=’main.css‘)}}»>
<script src=»/static/javascript/javascript.js»>
</script>
</head>
<body>
<img src=»/static/images/kalyani.jpg» height=»200″ />
<h1 class=»styled»>
Kalyani’
s Resume
</h1>
This page will contain my resume
<br>
<br>
<id=«para»> </p>
<button id=«button» type=«button» onclick=«myFunction()»> Click to see Resume </button>
</body>
</html>

В качестве альтернативы вы также можете использовать это: <script src = ”{{url_for (‘static’, filename = ‘javascript / javascript.js’)}}”> </script>. Последний сгенерирует этот HTML-код:

<!DOCTYPE html>
<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<title>Kalyani‘s Resume</title>
<link rel=»stylesheet» type=»text/css» href=»{{ url_for(‘
static‘, filename=’main.css‘)}}»>

<script src=»{{ url_for(‘static‘, filename=’javascript/javascript.js‘)}}»>
</script>
</head>
<body>
<img src=»{{ url_for(‘
static‘, filename=’images/kalyani.jpg‘)}}» height=»200″ />

<h1 class=»styled»>
Kalyani’s Resume
</h1>
This page will contain my resume
<br>
<br>
<id=«para»> </p>
<button id=«button» type=«button» onclick=«myFunction()»> Click to see Resume </button>
</body>
</html>

Заключение

Flask — это микро-фреймворк, который удобен в использовании и отлично подходит для начинающих. В частности, сама документация великолепна, и ее можно найти по адресу https://flask.palletsprojects.com/en/2.0.x/quickstart/#static-files. В этом руководстве мы узнали, как создать простой веб-сайт, добавить CSS, добавить изображения и добавить JavaScript на веб-сайт с помощью модуля Python Flask. Мы надеемся, что эта статья оказалась для вас полезной, и, пожалуйста, ознакомьтесь с Linux Hint для получения более информативных статей.

Рассказываем про один из самых популярных и лаконичных микрофреймворков для Python — Flask. Как написать простое приложение, подключить к нему Bootstrap и базу данных, и есть ли вообще у Flask минусы.

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

Первая публичная версия Flask вышла 16 апреля 2010 года. Автор проекта — Армин Ронахер, который возглавлял команду энтузиастов в Python-разработке Poocco. Flask основан на быстром и расширяемом механизме шаблонов Jinja и наборе инструментов Werkzeug. Кроме того, Flask использует одну из самых передовых служебных библиотек WSGI (Web Server Gateway Interface — стандарт взаимодействия между Python-программой, выполняющейся на стороне сервера, и самим веб-сервером).

При этом WSGI тоже разработал Армин Ронахер. По его словам, идея Flask изначально была первоапрельской шуткой, которая стала популярной и превратилась в серьезное приложение.

Изучите Python на Хекслете
Пройдите нашу профессию «Python-разработчик», чтобы поменять свою жизнь и стать бэкенд-программистом.

Плюсы и минусы Flask

Практически все плюсы и минусы Flask появились именно из-за того, что он является микрофреймворком.

Среди достоинств:

  • Простота. Flask легко установить и настроить.
  • Гибкость. Микрофреймворк позволяет разработчикам самостоятельно выбирать технологии и инструменты, которые они хотят применять в своих проектах
  • Расширяемость. Flask позволяет расширять функциональность с помощью плагинов и модулей, которые можно легко интегрировать в проект.
  • Активное сообщество. Flask является одним из самых используемых фреймворков для Python, поэтому имеет большое комьюнити разработчиков.

При этом у Flask есть и свои недостатки:

  • Отсутствие готовых решений. Разработчики изначально могут использовать во Flask только минимальный набор функциональности. Если же программисту нужны более широкие возможности, такие как аутентификация пользователя, ему придется добавить дополнительные библиотеки или реализовать это самостоятельно.
  • Нет встроенной многопоточности. Flask был разработан как однопоточный фреймворк. И чтобы управлять многопоточными веб-приложениями, придется установить дополнительные библиотеки.
  • Ограниченные возможности для масштабирования. Если проект начинает расти и усложняться, то могут появиться сложности в поддержке нужной функциональности.

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

Как создать проект на Flask

Для начала работы с микрофреймворком нужно скачать последнюю версию Flask:

pip install Flask

Для примера мы напишем на Flask тестовое веб-приложение с минимальным функционалом. Как работают приложения такого типа:

  • Пользователь вводит в браузере url, например — hexlet.io. В нашем тестовом приложении пользователь не будет вводить url, потому что мы будем работать с локальным сервером по адресу http://127.0.0.1:5000.
  • Браузер получает у DNS IP-адрес нужного нам сервера. DNS — это Domain Name System, распределенная системе серверов. Она работает как общая «контактная книга» в интернете.
  • Браузер отправляет запрос по этому адресу и получает ответ. Как правило — в виде HTML-страницы.
  • Браузер отображает содержимое страницы.

Итак, создадим файл hello.py и запишем в него следующий код:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index() -> str:
    return '<p>Hello Flask!</p>'

if __name__ == '__main__':
    app.run(debug=True)

Давайте подробно разберем, что делает код, который мы написали.

Первой строкой мы импортируем класс Flask. После этого мы создаем объект этого класса, передав первым аргументом имя модуля, — это и будет наше приложение для общения с веб-cервером. __name__ — это удобный способ передать именно то приложение, из которого запущен Flask.

Декоратор route() сообщает Flask, при обращении к какому URL-адресу запустится декорируемая разработчиком функция — в нашем примере это index. Последней строчкой мы открываем локальный веб-сервер с параметром debug=True — это позволит следить за всеми ошибками в логе программы.

Читайте также:
Программирование на Python: особенности обучения, перспективы, ситуация на рынке труда

Запускаем веб-приложение через терминал:

python hello.py

Если мы все сделали правильно, то в терминале появятся эти сообщения:

Сообщение в терминале об удачном запуске приложения

В основном тут отображается служебная информация. Единственное, что нас интересует — сообщение, что наш локальный сервер запущен по адресу http://127.0.0.1:5000/. В нем красными буквами указывается, что локальный сервер не подходит для продакшена. Но, так как мы реализовали тестовое приложение, то не будем деплоить его на реальный сервер.

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

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    greet_with_link = """<h1>Привет, Мир!</h1>
    <p><a href="user/Аникин/Георгий">Нажми на меня</a></p>"""
    return greet_with_link

@app.route('/user/<surname>/<name>')
def get_user(name, surname):
    personal_instruc = f"""<h1>Привет, {surname} {name}!</h1>
    <p>Измени имя пользователя в адресной строке и перезагрузи страницу</p>"""
    return personal_instruc

if __name__ == '__main__':
    app.run(debug=True)

В нашем примере значения просто появятся в браузере как часть строки. На стартовой странице нашего сайта будет запускаться функция index(). В ней пользователю, помимо приветствия, будет предлагаться нажать на ссылку, при клике на которую он перейдет на user/Аникин/Георгий. Этот URL-маршрут будет обрабатываться уже функцией get_user.

Функция get_user декорируется @app.route('/<surname>/<name>’), а в адресной строке у нас /user/Аникин/Георгий. То есть наша функция получает аргументы из URL-адреса, эти значения лежат между косых скобок. По умолчанию тип таких значений string принимает любой текст без косой черты. Но переменные маршрутов могут быть и иных типов: int, float, path и других. Типы указываются в формате <тип:имя переменной>.

Структура приложения на Flask

Создадим подкаталог flask_app с такой структурой файлов и папок:

Структура файлов в проекте

Чтобы написать приложение сложнее одной строки, в директории проекта должны находиться папки static и templates. Директория static содержит ресурсы, которые используются шаблонами. В том числе включая файлы CSS, JavaScript и картинки. Папка templates содержит только шаблоны с расширением *.html.

Заполним наши файлы кодом. Сначала — наш основной файл проекта app.py:

from flask import Flask, render_template

app = Flask(__name__)

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


@app.route("/about")
def get_page_about():
    return render_template("about.html")

if __name__ == "__main__":
    app.run(debug=True)

После этого — index.html:

<!DOCTYPE html>
<html>
    <head>
        <title>Main page</title>
    </head>
    <body>
        <h1>Главная страница</h1>
    </body>
</html>

И файл about.html:

<!DOCTYPE html>
<html>
    <head>
        <title>About</title>
    </head>
    <body>
        <h1>О приложении</h1>
    </body>
</html>

Для отображения HTML-шаблонов мы используем функцию render_template(). В нашем коде она принимает только имя шаблона и возвращает строку с результатом рендеринга шаблона.

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

Напишем базовый шаблон base.html и пару его наследников. При этом блоки {% block smth %} … {% endblock %} — это части базового шаблона, которые можно заменить в наследнике. Переменные передаются по именам в конструкции {{ variable }}.

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Приложение Flask{% endblock %}</title>
</head>
<body>
    <h1>{{h1}}</h1>
</body>
</html>

После появления файла с базовым HTML-шаблоном можем поправить наши остальные HTML-файлы:

about.html:
{% extends 'base.html' %}
{% block title %}About{% endblock %}

index.html:
{% extends 'base.html' %}
{% block title %}Main page{% endblock %}

Кроме того, нужно поправить и основной файл Flask-проекта app.py:

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html", h1 = "Главная страница")

@app.route("/about")
def get_page_about():
    return render_template("about.html", h1 = "О приложении")

if __name__ == "__main__":
    app.run(debug=True)

Подключаем Bootstrap

Bootstrap — это открытый и бесплатный набор инструментов для создания сайтов и веб-приложений.

В нашем проекте в папке templates у нас есть подкаталог bootstrap, а в нем файл base.html — это немного модифицированная заготовка сайта-документации Bootstrap-Flask:

<!doctype html>
<html lang="en">
    <head>
        {% block head %}
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        {% block styles %}
            <!-- Bootstrap CSS -->
            {{ bootstrap.load_css() }}
        {% endblock %}

        <title>{% block title %}Приложение Flask{% endblock %}</title>
        {% endblock %}
    </head>
    <body>
        <!-- Your page content -->
        {% block content %}
            <div class="jumbotron text-center">
            <h1>{{h1}}</h1>
            </div>
        {% endblock %}

        {% block scripts %}
            <!-- Optional JavaScript -->
            {{ bootstrap.load_js() }}
        {% endblock %}
    </body>
</html>

В файлах index.html и about.html заменим строку наследования на:

{% extends 'bootstrap/base.html' %}

Второй путь подключения Bootstrap к проекту на Flask — через CDN. Подробнее об этом можно почитать в документации фреймворка.

Читайте также:
Как создатель Python Гвидо ван Россум устроился в Microsoft и теперь работает над развитием CPython

После подключения Bootstrap нужно будет немного поправить основной файл нашего проекта app.py:

from flask_bootstrap import Bootstrap4
from flask import Flask, render_template

app = Flask(__name__)
bootstrap = Bootstrap4(app)

@app.route("/")
def index():
    return render_template("index.html", h1 = "Главная страница")

@app.route("/about")
def get_page_about():
    return render_template("about.html", h1 = "О приложении")

if __name__ == "__main__":
    app.run(debug=True)

Последним элементом нашего веб-приложения будет форма отправки. Для этого нужно немного модифицировать index.html:

{% extends 'bootstrap/base.html' %}
{% block title %}Main page{% endblock %}
{% block content %}
   {{super()}}
   <div class="container text-center">
       <form class="d-inline-block" style="max-width: 33%;">
           <div class="form-group">
               <label for="eventDate">Дата</label>
               <input type="date" name="eventDate" class="form-control" placeholder="Дата события">
           </div>
           <div class="form-group">
               <label for="eventName">Событие</label>
               <input type="text" name="eventName" class="form-control" placeholder="Название события">
           </div>
           <div class="form-group">
               <label for="eventDuration">Продолжительность</label>
               <input type="number" name="eventDuration" class="form-control" placeholder="Продолжительность" min="1" max="24">
           </div>
           <button type="submit" class="btn btn-primary">Записать</button>
       </form>
   </div>
{% endblock %}

Вообще, Bootstrap может добавить огромное количество элементов в приложение буквально в несколько кликов. Мы ограничились четырьмя — три поля и одна кнопка. Ключевой элемент здесь — это {{ super() }}.

Подключаем базу данных

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

Есть большое количество способов работы с SQL-запросами во Flask. Мы можем использовать, например, sqlite3 и чистый SQL, а можем — библиотеку sqlite3 для Python. Кроме того, можно обернуть чистые SQL-запросы в код, либо использовать Psycopg2 для работы с PostgresSQL в Python (мы рекомендуем делать именно так и вот почему). Для примера в этом тексте мы используем библиотеку Flask SQLAlchemy (расширение для Flask), которая предлагает технологию ORM для взаимодействия с БД.

Подключаем базу данных к нашему проекту через файл app.py:

from datetime import datetime


from flask import Flask, redirect, render_template, request
from flask_bootstrap import Bootstrap4
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
bootstrap = Bootstrap4(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///events.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class Event(db.Model):
   id = db.Column(db.Integer, primary_key=True)
   date = db.Column(db.Date, nullable=False)
   name = db.Column(db.String(255), nullable=False)
   duration = db.Column(db.Integer, nullable=False)

   def __str__(self):
       return (
           f"Название: {self.name}n"
           f"Дата: {self.date}n"
           f"Продолжительность {self.duration}ч"
       )


@app.route('/', methods=['POST'])
def add_event():
   date = datetime.strptime(request.form['eventDate'], '%Y-%m-%d').date()
   name = request.form['eventName']
   duration = int(request.form['eventDuration'])
   print(date, name, duration, sep='n')
   event = Event(date=date, name=name, duration=duration)
   db.session.add(event)
   db.session.commit()
   return redirect('/')


@app.route("/")
def index():
   return render_template("index.html", h1 = "Главная страница")


@app.route("/about")
def get_page_about():
   return render_template("about.html", h1 = "О приложении")


if __name__ == "__main__":
   with app.app_context():
       db.create_all()
   app.run(debug=True)

В нашей БД появился класс Event c атрибутами, который наследуется от db.Model. Это позволяет с помощью SQLAlchemy создать таблицу event, а поля нашего класса сделать колонками этой таблицы. Кроме того, мы определили магический метод __str__ для строкового отображения экземпляров класса — это пригодится для отображения в HTML.

Для создания таблицы в блок if __name__ == ‘__main__’ мы добавили команду db.create_all(), а для обработки отправленной формы — метод add_event. Он работает с методом POST, который указывает Flask, что данные будут отправлены на сервер.

В методе POST мы считываем данные отправленной формы и создаем для каждой строки временную переменную. После этого мы создаем объект event класса Event, передаем наши временные переменные как именованные аргументы, добавляем event в БД и фиксируем изменения.

Нам осталось лишь немного поправить форму: в файле index.html в открывающем теге <form> добавим атрибуты action="{{ url_for('add_event') }}" method="POST". Теперь форма отправки по нажатию на кнопку «Записать» будет отправлять данные в базу данных.

Добавим страницу отображения наших записей в новый файл Events.html:

{% extends 'bootstrap/base.html' %}
{% block title %}Events{% endblock %}
{% block content %}
   {{super()}}
   <div class="container text-center">
       <a href="{{ url_for('index') }}"><h2>Добавить событие</h2></a>
   </div>
   <div class="mt-4">
       <ul class="list-group">
           {% for event in events %}
               <li class="list-group-item">
                   <p>{{ event }}</p>
               </li>
           {% endfor %}
       </ul>
   </div>
{% endblock %}

В файл app.py добавим view:

@app.route("/events")
def view_events():
   events = Event.query.order_by(Event.date).all()
   return render_template("events.html", h1 = "События", events=events)

А в основном контейнере index.html добавим ссылку на эту страницу:

<a href="{{ url_for('view_events') }}"><h2>Посмотреть события</h2></a>

Наш тестовый проект на Flask готов! Его можно запустить на локальном сервере через команду python app.py (в некоторых случаях надо будет написать название директории перед названием файла app.py).

Что еще почитать про Flask

  • Большой курс по Flask на Хекслете
  • Документация Flask
  • Цикл статей на Real Python
  • Проектирование RESTful API с помощью Python и Flask

Изучите Python на Хекслете
Пройдите нашу профессию «Python-разработчик», чтобы поменять свою жизнь и стать бэкенд-программистом.

Стать бэкенд-разработчиком

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

Сегодня я расскажу про то, как написать Hello world, как CGI-скрипт.

Настройка локального сервера

В Python уже есть встроенный CGI сервер, поэтому его настройка элементарна.

Для запуска из консоли (для любителей linux-систем). Запускать нужно из той папки, где мы хотим работать:

python3 -m http.server --cgi

Для сидящих на Windows чуть проще будет запуск Python файла (заметьте, что он должен находиться в той же папке, в которой мы планируем работать!):

from http.server import HTTPServer, CGIHTTPRequestHandler
server_address = ("", 8000)
httpd = HTTPServer(server_address, CGIHTTPRequestHandler)
httpd.serve_forever()

Теперь откройте браузер и в адресной строке наберите localhost:8000

Если у вас примерно такая же картина, значит, у вас все заработало!

Локальный CGI-сервер

Теперь в той папке, где мы запустили сервер, создаём папку cgi-bin (у меня она уже создана).

В этой папке создаём скрипт hello.py со следующим содержимым:

#!/usr/bin/env python3

print("Content-type: text/html")
print()
print("<h1>Hello world!</h1>")

Первая строка говорит о том, что это Python скрипт (CGI-скрипты можно не только на Python писать).

Вторая строка печатает заголовок. Он обозначает, что это будет html файл (бывает ещё css, javascript, pdf и куча других, и браузер различает их по заголовкам).

Третья строка (просто символ новой строки) отделяет заголовки от тела ответа.

Четвёртая печатает Hello world.

Теперь переходим на localhost:8000/cgi-bin/hello.py

И радуемся!

Hello world CGI

Если у вас не работает, проверьте, установлены ли права на выполнение.

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

CGI ошибки

В следующей части мы рассмотрим обработку данных форм и cookies.

Для вставки кода на Python в комментарий заключайте его в теги <pre><code class=»python3″>Ваш код</code></pre>

Понравилась статья? Поделить с друзьями:
  • Создание сайта на wix пошаговая инструкция
  • Создание сайта на php с нуля самостоятельно пошаговая инструкция
  • Создание сайта на html и css с нуля самостоятельно пошаговая инструкция
  • Создание сайта на google sites инструкция
  • Создание сайта на beget пошаговая инструкция