Фреймворк Flask

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

Flask был создан Армином Ронахером в 2010 году как «микрофреймворк», что означает его минималистичный подход к веб-разработке. Flask предоставляет только базовые функции, такие как маршрутизация URL и обработка HTTP-запросов, оставляя на выбор разработчика использование сторонних библиотек для добавления необходимых функций, таких как работа с базами данных, аутентификация пользователей и управление сессиями.

Основные особенности Flask:

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

Пример простого приложения на Flask:

from flask import Flask

app = Flask(__name__)

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

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

Этот код создает простое веб-приложение, которое выводит «Hello, World!» при обращении к корневому URL (/). Чтобы запустить приложение, сохраните код в файл app.py и выполните команду python app.py. Приложение будет доступно по адресу http://localhost:5000.

Задание 1. Создайте и запустите свое первое приложение на Flask, используя приведенный пример. Измените приветственное сообщение и добавьте новый маршрут (/about), который будет отображать информацию о вас или вашем проекте.

Содержание

Архитектура Flask и его преимущества

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

  1. Приложение Flask: В основе каждого проекта на Flask лежит экземпляр класса Flask, который представляет собой веб-приложение. Этот объект управляет маршрутизацией запросов, настройками конфигурации и запуском сервера.
  2. Маршрутизация: Flask использует декораторы для маршрутизации URL. Каждый маршрут связывается с определенной функцией, которая обрабатывает запросы и возвращает ответ. Например, декоратор @app.route('/') связывает корневой URL с функцией hello, которая возвращает строку «Hello, World!».
  3. Шаблоны: Flask поддерживает работу с шаблонами с помощью встроенного движка Jinja2. Это позволяет отделить логику приложения от представления (HTML-кода), создавая динамические веб-страницы.
  4. Расширения: Flask можно расширить с помощью множества готовых расширений для добавления функционала, такого как работа с базами данных, аутентификация пользователей и управление сессиями. Это позволяет использовать Flask для создания проектов любой сложности.

Преимущества Flask:

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

Пример использования шаблонов:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('home.html', title="Home Page")

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

Настройка и конфигурация проекта Flask

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

Шаг 1: Создание структуры проекта

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

my_flask_project/
│
├── app/
│   ├── static/
│   │   ├── css/
│   │   ├── js/
│   │   └── images/
│   ├── templates/
│   │   ├── base.html
│   │   └── home.html
│   ├── __init__.py
│   ├── routes.py
│   └── models.py
│
├── venv/
│   └── ... (файлы виртуального окружения)
│
├── config.py
├── .env
├── requirements.txt
└── run.py

Описание директорий и файлов:

  • app/: Директория, содержащая основное приложение.
    • static/: Здесь хранятся статические файлы, такие как CSS, JavaScript и изображения.
    • templates/: Папка для HTML-шаблонов.
    • __init__.py: Инициализирует приложение Flask и объединяет различные компоненты приложения.
    • routes.py: Здесь определяются маршруты (URL) и связанные с ними функции обработки запросов.
    • models.py: Файл для описания моделей базы данных, если вы используете ORM (например, SQLAlchemy).
  • venv/: Виртуальное окружение, в котором установлены зависимости проекта.
  • config.py: Файл конфигурации, где хранятся настройки приложения.
  • .env: Файл для хранения переменных окружения, таких как секретные ключи и параметры базы данных.
  • requirements.txt: Файл со списком зависимостей проекта, который можно использовать для установки необходимых библиотек с помощью pip.
  • run.py: Основной файл для запуска приложения.

Шаг 2: Настройка виртуального окружения

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

Настройка виртуального окружения:

  1. В терминале перейдите в директорию вашего проекта:
    cd my_flask_project
    
  2. Создайте виртуальное окружение:
    python -m venv venv
    
  3. Активируйте виртуальное окружение:
    • На Windows:
      venv\Scripts\activate
      
    • На macOS/Linux:
      source venv/bin/activate
      
  4. Установите Flask:
    pip install flask
    
  5. Сохраните зависимости в файл requirements.txt:
    pip freeze > requirements.txt
    

Шаг 3: Конфигурация проекта

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

Создание файла конфигурации config.py:

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
    DEBUG = False
    TESTING = False

class DevelopmentConfig(Config):
    DEBUG = True

class TestingConfig(Config):
    TESTING = True

class ProductionConfig(Config):
    DEBUG = False

Описание классов:

  • Config: Базовый класс конфигурации, который содержит общие настройки для всех окружений.
  • DevelopmentConfig: Конфигурация для разработки, включает отладочный режим.
  • TestingConfig: Конфигурация для тестирования, включает тестовый режим.
  • ProductionConfig: Конфигурация для продакшена, отключает отладку.

Использование конфигурации в приложении:

В файле __init__.py можно использовать настройки конфигурации, подключив их к приложению:

from flask import Flask
from config import DevelopmentConfig

app = Flask(__name__)
app.config.from_object(DevelopmentConfig)

from app import routes

Шаг 4: Работа с переменными окружения

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

Пример файла .env:

SECRET_KEY=super-secret-key
DATABASE_URL=sqlite:///site.db

Для использования переменных окружения в Flask можно использовать библиотеку python-dotenv. Установите ее:

pip install python-dotenv

И добавьте следующий код в run.py:

from dotenv import load_dotenv
import os

load_dotenv()  # Загружает переменные окружения из файла .env

Теперь переменные из .env будут доступны через os.environ.

Шаг 5: Запуск приложения

После настройки структуры и конфигурации, вы можете запустить приложение, используя файл run.py:

from app import app

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

Задание 2. Настройка проекта: Создайте проект на Flask, используя приведенную выше структуру директорий. Настройте виртуальное окружение, установите Flask и сохраните зависимости в requirements.txt.

Задание 3. Конфигурация: Создайте файл config.py с настройками для различных окружений (разработка, тестирование, продакшн). Подключите конфигурацию к вашему приложению.

Задание 4. Переменные окружения: Создайте файл .env и настройте использование переменных окружения для хранения чувствительных данных, таких как SECRET_KEY.

Задание 5. Запуск приложения: Напишите код для запуска приложения в run.py и убедитесь, что все работает корректно.

Создание первого приложения на Flask

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

Шаг 1: Подготовка проекта

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

  1. Создали структуру проекта, описанную в предыдущем разделе.
  2. Настроили виртуальное окружение и установили Flask.
  3. Настроили конфигурацию проекта в файле config.py.

После этого откройте файл app/__init__.py, который инициализирует ваше приложение Flask:

from flask import Flask

app = Flask(__name__)
app.config.from_object('config.DevelopmentConfig')  # Используем конфигурацию для разработки

from app import routes  # Импортируем маршруты

Шаг 2: Создание маршрутов (routes)

Маршруты (или URL-маршрутизация) в Flask определяют, какие функции будут вызваны при обращении пользователя к определенным URL. Создадим несколько маршрутов в файле app/routes.py.

Пример маршрутов:

from flask import render_template, flash, redirect, url_for
from app import app
from app.forms import LoginForm  # Импортируем форму

@app.route('/')
@app.route('/index')
def index():
    user = {'username': 'John'}
    posts = [
        {'author': {'username': 'Susan'}, 'body': 'Beautiful day in Portland!'},
        {'author': {'username': 'Mary'}, 'body': 'The Avengers movie was awesome!'}
    ]
    return render_template('index.html', title='Home', user=user, posts=posts)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        flash(f'Login requested for user {form.username.data}, remember_me={form.remember_me.data}')
        return redirect(url_for('index'))
    return render_template('login.html', title='Sign In', form=form)

Что происходит в этом коде:

  • Маршруты / и /index привязаны к функции index(), которая рендерит шаблон index.html, передавая туда переменные user и posts.
  • Маршрут /login обрабатывает GET и POST-запросы и отображает форму для входа. При успешной отправке формы пользователь перенаправляется на главную страницу с уведомлением.

Шаг 3: Создание шаблонов

Шаблоны в Flask позволяют отделить HTML-код от логики Python. Они используются для генерации динамического содержимого страниц.

Создадим два шаблона: base.html (базовый шаблон) и index.html (главная страница).

templates/base.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }} - My Flask App</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
  </head>
  <body>
    <div class="container">
      <h1>Welcome to My Flask App</h1>
      {% block content %}{% endblock %}
    </div>
  </body>
</html>

templates/index.html:

{% extends "base.html" %}

{% block content %}
  <h2>Hello, {{ user.username }}!</h2>
  <ul>
    {% for post in posts %}
      <li><strong>{{ post.author.username }}:</strong> {{ post.body }}</li>
    {% endfor %}
  </ul>
{% endblock %}

Что происходит в этих шаблонах:

  • base.html — это базовый шаблон, который включает в себя основной каркас HTML-документа и стили. Внутри него используется блок content, который будет заменен содержимым дочерних шаблонов.
  • index.html расширяет base.html, заполняя блок content списком постов и приветствием пользователя.

Шаг 4: Обработка форм

Для работы с формами в Flask удобно использовать библиотеку Flask-WTF, которая упрощает создание и валидацию форм.

Установка Flask-WTF:

pip install flask-wtf

Создание формы входа в систему:

Создадим файл app/forms.py, где будет определена форма для входа:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')

Форма в шаблоне login.html:

Создадим шаблон для отображения формы входа:

{% extends "base.html" %}

{% block content %}
  <h2>Sign In</h2>
  <form action="" method="post">
    {{ form.hidden_tag() }}
    <p>
      {{ form.username.label }}<br>
      {{ form.username(size=32) }}<br>
    </p>
    <p>
      {{ form.password.label }}<br>
      {{ form.password(size=32) }}<br>
    </p>
    <p>
      {{ form.remember_me() }} {{ form.remember_me.label }}<br>
    </p>
    <p>{{ form.submit() }}</p>
  </form>
{% endblock %}

Что здесь происходит:

  • LoginForm — это форма, которая создается с помощью Flask-WTF. Она содержит поля для ввода имени пользователя, пароля и флажок для запоминания пользователя.
  • Шаблон login.html отображает эту форму и обрабатывает ее отправку.

Шаг 5: Запуск приложения

После настройки всех компонентов, запустим наше приложение. Откройте терминал, активируйте виртуальное окружение (если оно еще не активировано), и выполните команду:

python run.py

Перейдите в браузере по адресу http://localhost:5000 и протестируйте ваше приложение. Вы увидите главную страницу с приветствием и списком постов, а также страницу входа с формой.

Функция url_for

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

Как работает url_for?

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

Пример маршрута и использования url_for:

from flask import Flask, url_for

app = Flask(__name__)

@app.route('/')
def home():
    return 'Home Page'

@app.route('/about')
def about():
    return 'About Page'

@app.route('/user/<username>')
def user_profile(username):
    return f'User: {username}'

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

Чтобы сгенерировать ссылки на эти страницы, мы можем использовать url_for:

with app.test_request_context():
    print(url_for('home'))  # Выводит: /
    print(url_for('about'))  # Выводит: /about
    print(url_for('user_profile', username='John'))  # Выводит: /user/John

Преимущества использования url_for

  1. Динамическое создание URL: Если маршрут изменится, не нужно будет искать и исправлять все ссылки вручную — достаточно обновить маршрут в одном месте.
  2. Передача параметров: Легко работать с динамическими параметрами (например, username, id).
  3. Создание ссылок на статические файлы: Можно использовать url_for для генерации URL для статических файлов (CSS, JS, изображения).

Пример использования для статических файлов:

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

Практическое применение url_for

Представим, что у нас есть приложение с маршрутом профиля пользователя. Чтобы создать ссылку на этот профиль, мы можем использовать url_for:

В шаблоне HTML:

<a href="{{ url_for('user_profile', username='Alice') }}">View Profile</a>

В Python-коде:

@app.route('/dashboard')
def dashboard():
    profile_url = url_for('user_profile', username='Alice')
    return f'<a href="{profile_url}">Go to Alice\'s profile</a>'

Таким образом, url_for помогает избежать жесткой привязки к URL в коде и поддерживать гибкость приложения.

Практические задания

Задание 6. Создание нового маршрута: Добавьте новый маршрут /about, который будет отображать информацию о проекте или о вас. Создайте соответствующий шаблон about.html и подключите его.

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

Задание 8. Статические файлы: Добавьте стили CSS в папку static/css, например, файл style.css, и подключите его к шаблону base.html. Создайте простое оформление для вашего приложения.

Работа с URL маршрутами и views

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

Шаг 1: Понимание маршрутов в Flask

В Flask маршруты задаются с помощью декораторов, которые привязывают определенные URL к функциям Python. Когда пользователь открывает в браузере определенный URL, Flask сопоставляет его с маршрутом и вызывает соответствующую функцию.

Пример простого маршрута:

@app.route('/')
def home():
    return "Welcome to the Home Page"

В этом примере функция home() связана с корневым URL (/). Когда пользователь переходит по адресу http://localhost:5000/, Flask вызывает эту функцию и возвращает строку «Welcome to the Home Page».

Шаг 2: Параметры маршрутов

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

Пример с обязательным параметром:

@app.route('/user/<username>')
def show_user_profile(username):
    return f'User: {username}'

Здесь username — это параметр, который будет передан в функцию show_user_profile. Если пользователь перейдет по адресу http://localhost:5000/user/John, Flask передаст значение «John» в функцию, и браузер отобразит «User: John».

Пример с необязательным параметром:

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'Post {post_id}'

В этом случае параметр post_id должен быть целым числом. Если пользователь перейдет по адресу http://localhost:5000/post/5, функция show_post получит значение 5.

Шаг 3: Использование функции url_for

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

Пример использования url_for:

from flask import url_for

@app.route('/')
def home():
    return f'<a href="{url_for("show_user_profile", username="John")}">John\'s Profile</a>'

Здесь url_for("show_user_profile", username="John") создаст ссылку на профиль пользователя John.

Шаг 4: Организация views

В Flask views — это функции, которые обрабатывают запросы и возвращают ответы клиенту. Ответы могут быть простыми строками, HTML-страницами, JSON-данными и т.д. В более сложных приложениях views могут быть организованы по модулям или классам, чтобы код оставался чистым и поддерживаемым.

Пример простого view:

@app.route('/greet/<name>')
def greet(name):
    return f'Hello, {name}!'

Организация views по модулям:

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

Пример:

Создадим новый файл app/user_routes.py:

from flask import Blueprint

bp = Blueprint('user', __name__)

@bp.route('/user/<username>')
def show_user_profile(username):
    return f'User: {username}'

Теперь в файле __init__.py нужно подключить этот модуль:

from flask import Flask
from app.user_routes import bp as user_bp

app = Flask(__name__)
app.register_blueprint(user_bp)

Это позволяет организовать код по модулям и поддерживать чистую архитектуру приложения.

Шаг 5: Обработка HTTP-методов

По умолчанию Flask маршруты обрабатывают только GET-запросы, но вы можете настроить маршрут для обработки других HTTP-методов, таких как POST, PUT, DELETE и т.д.

Пример обработки POST-запросов:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        return f'Logged in as {username}'
    return render_template('login.html')

Здесь маршрут /login обрабатывает как GET, так и POST-запросы. Если запрос GET, возвращается форма для входа. Если POST — данные из формы обрабатываются, и пользователь авторизуется.

Blueprints

Blueprints во Flask — это способ разделить ваше приложение на более мелкие, модульные части. Они позволяют организовать маршруты, представления (views) и другие компоненты по отдельным модулям. Это особенно полезно для крупных проектов, где один файл app.py становится слишком громоздким.

Зачем нужны blueprints?

  1. Модульность: Вы можете разделить разные части приложения (например, разделы для пользователей, продуктов и т.д.) в отдельные blueprints.
  2. Повторное использование: Blueprints можно легко повторно использовать в разных проектах.
  3. Гибкость: Они позволяют вам работать над отдельными частями приложения независимо, что упрощает поддержку и масштабирование.

Создание и использование blueprints

  1. Создание blueprints
    Чтобы создать blueprint, нужно импортировать класс Blueprint и настроить его:
    from flask import Blueprint
    
    # Создаем blueprint для управления пользователями
    user_bp = Blueprint('user_bp', __name__)
    
    @user_bp.route('/user/<username>')
    def user_profile(username):
        return f'Profile page of user {username}'
    
  2. Регистрация blueprints
    После создания blueprint его нужно зарегистрировать в основном приложении Flask:
    from flask import Flask
    from user_module import user_bp  # Импортируем blueprint
    
    app = Flask(__name__)
    
    # Регистрируем blueprint
    app.register_blueprint(user_bp)
    
    if __name__ == '__main__':
        app.run(debug=True)
    

Теперь при переходе по URL /user/<username> приложение будет использовать маршрут из blueprint.

Пример структуры приложения с blueprints

Пример структуры проекта, использующего blueprints:

my_flask_app/
│
├── app/
│   ├── __init__.py  # Инициализация основного приложения и регистрация blueprints
│   ├── user/
│   │   ├── __init__.py  # Blueprint для пользователей
│   │   ├── routes.py  # Маршруты, связанные с пользователями
│   ├── product/
│   │   ├── __init__.py  # Blueprint для продуктов
│   │   └── routes.py  # Маршруты, связанные с продуктами

Файл app/user/routes.py (blueprint для пользователей):

from flask import Blueprint

user_bp = Blueprint('user_bp', __name__)

@user_bp.route('/profile/<username>')
def profile(username):
    return f'User Profile for {username}'

Файл app/__init__.py:

from flask import Flask
from app.user.routes import user_bp
from app.product.routes import product_bp

app = Flask(__name__)

# Регистрация blueprints
app.register_blueprint(user_bp, url_prefix='/user')
app.register_blueprint(product_bp, url_prefix='/product')

Теперь URL /user/profile/<username> будет обрабатываться blueprint для пользователей, а URL /product/<product_id> — blueprint для продуктов.

Использование url_for с blueprints

Когда используете url_for в приложении с blueprints, нужно указать имя blueprint при создании URL.

Пример:

url_for('user_bp.profile', username='Alice')

Здесь user_bp.profile указывает, что маршрут принадлежит blueprint user_bp, а profile — это имя функции маршрута.

Практическое применение blueprints

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

Пример с префиксом маршрутов:

app.register_blueprint(user_bp, url_prefix='/users')

Все маршруты внутри user_bp будут доступны по адресу /users/....

Практические задания:

Задание 9. Создание динамических маршрутов: Создайте маршрут /hello/<name>, который будет приветствовать пользователя по имени. Если пользователь переходит по адресу /hello/Alice, приложение должно отображать «Hello, Alice!».

Задание 10. Использование url_for в шаблонах: В вашем проекте создайте шаблон с навигацией, где ссылки на страницы создаются динамически с помощью url_for.

Задание 11. Модульная организация: Перенесите один из существующих маршрутов в отдельный модуль (например, app/user_routes.py) и подключите его к вашему приложению через Blueprint.

Задание 12. Обработка POST-запросов: Реализуйте форму обратной связи, которая отправляет данные на сервер методом POST. Создайте маршрут для обработки этой формы и отображения введенных данных пользователю.

Работа с шаблонами Jinja2

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

Шаг 1: Основы работы с Jinja2

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

Пример простого шаблона:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>Hello, {{ name }}!</h1>
  </body>
</html>

Здесь:

  • {{ title }} и {{ name }} — это выражения Jinja2, которые будут заменены значениями, переданными из Flask.
  • Шаблон — это обычный HTML-код с добавлением динамических конструкций Jinja2.

Шаг 2: Передача данных в шаблон

Для того чтобы передать данные из вашего Flask-приложения в шаблон, используется функция render_template. Эта функция позволяет передавать переменные, которые затем будут использоваться в шаблоне.

Пример передачи данных в шаблон:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html', title='Home Page', name='Alice')

В этом примере мы передаем две переменные title и name в шаблон index.html. Эти переменные будут заменены на «Home Page» и «Alice» соответственно.

Шаблон index.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>Hello, {{ name }}!</h1>
  </body>
</html>

Шаг 3: Использование циклов и условий

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

Циклы в Jinja2:

Часто нужно отобразить список данных на странице. Для этого используется цикл for.

Пример:

@app.route('/users')
def users():
    user_list = ['Alice', 'Bob', 'Charlie']
    return render_template('users.html', users=user_list)

Шаблон users.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>User List</title>
  </head>
  <body>
    <h1>Users:</h1>
    <ul>
      {% for user in users %}
        <li>{{ user }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

Здесь мы передаем список пользователей в шаблон, и с помощью цикла for выводим каждый элемент списка в отдельный элемент списка HTML (<li>).

Условия в Jinja2:

Условные конструкции if позволяют проверять значения переменных и в зависимости от этого отображать разные данные.

Пример:

@app.route('/status/<int:score>')
def status(score):
    return render_template('status.html', score=score)

Шаблон status.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Status</title>
  </head>
  <body>
    <h1>Your score: {{ score }}</h1>
    {% if score >= 50 %}
      <p>Congratulations, you passed!</p>
    {% else %}
      <p>Sorry, you failed.</p>
    {% endif %}
  </body>
</html>

Здесь шаблон выводит разные сообщения в зависимости от значения переменной score.

Шаг 4: Наследование шаблонов

Jinja2 поддерживает наследование шаблонов, что позволяет создавать базовые шаблоны (например, для шапки, подвала и структуры страницы), а затем расширять их в дочерних шаблонах.

Базовый шаблон base.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
  </head>
  <body>
    <header>
      <h1>My Website</h1>
    </header>
    <div class="content">
      {% block content %}{% endblock %}
    </div>
    <footer>
      <p>&copy; 2024 My Website</p>
    </footer>
  </body>
</html>

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

Дочерний шаблон home.html:

{% extends "base.html" %}

{% block content %}
  <h2>Welcome to the Home Page</h2>
  <p>This is the main content of the home page.</p>
{% endblock %}

Здесь мы расширяем шаблон base.html и заполняем блок content уникальным содержимым для главной страницы.

Шаг 5: Работа со статическими файлами

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

Пример подключения CSS-файла:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
  </head>
  <body>
    <h1>Hello, {{ name }}!</h1>
  </body>
</html>

В этом примере CSS-файл style.css будет загружен из папки static/css.

Практические задания

Задание 13. Создание страницы с циклом: Создайте маршрут /products, который передаст список товаров в шаблон, и отобразите их с помощью цикла в формате HTML-таблицы.

Задание 14. Условная логика: Добавьте в шаблон страницу, которая будет отображать разные сообщения в зависимости от переданных данных, например, статус «выиграл» или «проиграл».

Задание 15. Наследование шаблонов: Создайте базовый шаблон с шапкой и подвалом, а затем создайте несколько дочерних страниц (например, «Главная», «О нас», «Контакты»), которые будут расширять базовый шаблон.

Задание 16. Подключение CSS: Добавьте папку static, создайте файл стилей и подключите его к вашим страницам для улучшения их внешнего вида.

Подключение базы данных и работа с ORM

Веб-приложения часто нуждаются в хранении данных, и подключение базы данных — важный этап при разработке. Flask поддерживает интеграцию с различными базами данных, и для удобства работы с ними используется ORM (Object-Relational Mapping) — технология, позволяющая работать с базой данных через объекты Python. В этом разделе мы рассмотрим, как подключить базу данных к Flask, использовать ORM SQLAlchemy и управлять данными.

Шаг 1: Установка и настройка SQLAlchemy

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

Установка Flask-SQLAlchemy

Для начала установим необходимые зависимости:

pip install flask-sqlalchemy

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

SQLAlchemy поддерживает различные базы данных, такие как SQLite, PostgreSQL, MySQL и другие. В качестве примера, мы будем использовать SQLite, которая не требует установки дополнительного ПО и подходит для небольших проектов.

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

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
    SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'  # Подключение к базе данных SQLite
    SQLALCHEMY_TRACK_MODIFICATIONS = False  # Отключение предупреждений о модификациях

Теперь подключим базу данных к приложению. Откройте файл app/__init__.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

# Создаем объект SQLAlchemy
db = SQLAlchemy(app)

from app import routes, models

Шаг 2: Создание моделей

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

Создайте файл app/models.py и добавьте туда модель User:

from app import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def __repr__(self):
        return f'<User {self.username}>'

Здесь мы создали модель User, которая имеет следующие столбцы:

  • id: Первичный ключ (уникальный идентификатор).
  • username: Имя пользователя (уникальное).
  • email: Адрес электронной почты (уникальный).
  • password_hash: Хэш пароля.

Шаг 3: Создание базы данных

После создания моделей нужно создать таблицы в базе данных. SQLAlchemy может автоматически создать все необходимые таблицы на основе моделей.

Откройте интерактивную оболочку Python:

flask shell

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

from app import db
db.create_all()  # Создание таблиц в базе данных

Эта команда создаст таблицу User в базе данных app.db, которая была указана в настройках.

Шаг 4: Взаимодействие с базой данных

Теперь мы можем добавлять, изменять и удалять записи в базе данных с помощью моделей. Давайте рассмотрим, как работать с объектами модели User.

Добавление данных

Откройте оболочку Python и добавьте нового пользователя:

from app import db
from app.models import User

# Создаем нового пользователя
user = User(username='Alice', email='alice@example.com', password_hash='hashed_password')
db.session.add(user)
db.session.commit()  # Сохраняем изменения в базе данных

Получение данных

Для получения данных из базы используется метод query. Например, чтобы получить всех пользователей:

users = User.query.all()  # Получение всех пользователей
for user in users:
    print(user.username, user.email)

Чтобы найти конкретного пользователя по имени:

user = User.query.filter_by(username='Alice').first()
print(user)

Обновление данных

Для обновления данных достаточно изменить атрибуты объекта и сохранить изменения в базе данных:

user = User.query.filter_by(username='Alice').first()
user.email = 'newalice@example.com'
db.session.commit()  # Сохраняем изменения

Удаление данных

Чтобы удалить запись, используйте метод delete:

user = User.query.filter_by(username='Alice').first()
db.session.delete(user)
db.session.commit()  # Сохраняем изменения

Шаг 5: Отображение данных в шаблонах

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

В файле app/routes.py добавьте новый маршрут:

from app import app
from app.models import User
from flask import render_template

@app.route('/users')
def users():
    all_users = User.query.all()
    return render_template('users.html', users=all_users)

Теперь создайте шаблон templates/users.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>User List</title>
  </head>
  <body>
    <h1>Users</h1>
    <ul>
      {% for user in users %}
        <li>{{ user.username }} - {{ user.email }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

Перейдите по адресу http://localhost:5000/users, и вы увидите список всех пользователей, хранящихся в базе данных.

Шаг 6: Миграции базы данных

Когда ваша база данных разрастается, вам может потребоваться изменить структуру таблиц — например, добавить новое поле в таблицу. Для управления изменениями структуры базы данных в Flask используется инструмент Flask-Migrate, который основан на библиотеке Alembic.

Установка Flask-Migrate

Установите Flask-Migrate:

pip install flask-migrate

Добавьте в файл app/__init__.py следующие строки для инициализации миграций:

from flask_migrate import Migrate
from app import app, db

migrate = Migrate(app, db)

Создание миграций

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

  1. Инициализация миграций (один раз для проекта):
    flask db init
    
  2. Создание миграции (когда вы меняете модели):
    flask db migrate -m "Add user table"
    
  3. Применение миграции к базе данных:
    flask db upgrade
    

Практические задания:

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

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

Задание 19. Миграции: Добавьте новое поле к модели пользователя (например, дату регистрации) и используйте миграции для обновления базы данных.

Управление формами и валидация данных

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

В этом разделе мы рассмотрим, как создавать формы, проверять их корректность и обрабатывать ввод данных.

Шаг 1: Установка Flask-WTF

Для работы с формами установим расширение Flask-WTF:

pip install flask-wtf

Шаг 2: Настройка CSRF-защиты

Flask-WTF автоматически включает защиту от подделки межсайтовых запросов (CSRF), которая предотвращает отправку форм с других сайтов. Для работы CSRF-защиты необходимо задать секретный ключ для приложения.

Добавьте в файл config.py:

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'my_secret_key'
    SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

Теперь в app/__init__.py подключите настройки приложения:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

db = SQLAlchemy(app)

Шаг 3: Создание формы

WTForms предоставляет класс FlaskForm, с помощью которого можно создавать поля формы и задавать правила валидации.

Пример формы регистрации пользователя:

Создайте файл app/forms.py и добавьте код для формы:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Email, EqualTo

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    password2 = PasswordField('Repeat Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Register')

Описание полей формы:

  • StringField — поле для текста (имя пользователя и электронная почта).
  • PasswordField — поле для ввода пароля.
  • SubmitField — кнопка для отправки формы.
  • Валидаторы, такие как DataRequired(), Email() и EqualTo(), обеспечивают проверку корректности введенных данных.

Шаг 4: Создание маршрута для формы

Теперь создадим маршрут для отображения формы регистрации и обработки данных.

Добавьте в файл app/routes.py:

from flask import render_template, flash, redirect, url_for
from app import app
from app.forms import RegistrationForm

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        flash(f'Registration successful for user {form.username.data}')
        return redirect(url_for('index'))
    return render_template('register.html', title='Register', form=form)

В этом примере:

  • Маршрут /register обрабатывает как GET, так и POST-запросы.
  • Если форма проходит валидацию, на экране появляется всплывающее сообщение о том, что пользователь успешно зарегистрирован, и происходит перенаправление на главную страницу.
  • Если данные введены неверно, форма будет заново отображена с сообщениями об ошибках.

Шаг 5: Создание шаблона для формы

Создадим HTML-шаблон для отображения формы. Flask-WTF автоматически генерирует необходимые поля формы и скрытые токены для CSRF-защиты.

Шаблон templates/register.html:

{% extends "base.html" %}

{% block content %}
  <h2>Register</h2>
  <form method="POST" action="">
    {{ form.hidden_tag() }}
    <p>
      {{ form.username.label }}<br>
      {{ form.username(size=32) }}<br>
      {% for error in form.username.errors %}
        <span style="color: red;">[{{ error }}]</span>
      {% endfor %}
    </p>
    <p>
      {{ form.email.label }}<br>
      {{ form.email(size=32) }}<br>
      {% for error in form.email.errors %}
        <span style="color: red;">[{{ error }}]</span>
      {% endfor %}
    </p>
    <p>
      {{ form.password.label }}<br>
      {{ form.password(size=32) }}<br>
      {% for error in form.password.errors %}
        <span style="color: red;">[{{ error }}]</span>
      {% endfor %}
    </p>
    <p>
      {{ form.password2.label }}<br>
      {{ form.password2(size=32) }}<br>
      {% for error in form.password2.errors %}
        <span style="color: red;">[{{ error }}]</span>
      {% endfor %}
    </p>
    <p>{{ form.submit() }}</p>
  </form>
{% endblock %}

Здесь:

  • {{ form.hidden_tag() }} — используется для вставки скрытого CSRF-токена.
  • Для каждого поля формы выводятся возможные ошибки, если валидация не прошла.

Шаг 6: Валидация данных

Flask-WTF автоматически проверяет корректность данных при отправке формы. Если данные не соответствуют требованиям валидаторов, форма возвращается с сообщениями об ошибках.

Пример сообщений об ошибках:

  • Если поле «Email» не заполнено или заполнено некорректно, пользователь увидит сообщение об ошибке.
  • Если пароли не совпадают, появится ошибка, связанная с полем «Repeat Password».

Шаг 7: Расширение валидации

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

from wtforms.validators import ValidationError
from app.models import User

class RegistrationForm(FlaskForm):
    # другие поля...
    
    def validate_username(self, username):
        user = User.query.filter_by(username=username.data).first()
        if user is not None:
            raise ValidationError('Please use a different username.')
    
    def validate_email(self, email):
        user = User.query.filter_by(email=email.data).first()
        if user is not None:
            raise ValidationError('Please use a different email address.')

Здесь валидация проверяет, существует ли пользователь с таким именем или email, и если да, выводит сообщение с ошибкой.

Шаг 8: Пример формы для входа

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

Форма LoginForm:

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')

Маршрут для обработки формы входа:

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        flash(f'Login requested for user {form.username.data}, remember_me={form.remember_me.data}')
        return redirect(url_for('index'))
    return render_template('login.html', title='Sign In', form=form)

Практические задания

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

Задание 21. Расширенная валидация: Добавьте собственные валидаторы для проверки уникальности имени пользователя и email при регистрации.

Задание 22. Обработка ошибок: Выведите ошибки в форме регистрации и входа, если данные не соответствуют требованиям.

Обработка статических файлов

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

Шаг 1: Организация директории для статических файлов

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

Пример структуры проекта с папкой static:

my_flask_project/
│
├── app/
│   ├── static/
│   │   ├── css/
│   │   │   └── style.css
│   │   ├── js/
│   │   │   └── script.js
│   │   └── images/
│   │       └── logo.png
│   ├── templates/
│   │   └── base.html
│   ├── __init__.py
│   └── routes.py
│
├── config.py
└── run.py
  • css/: Папка для стилей CSS.
  • js/: Папка для JavaScript.
  • images/: Папка для изображений.

Шаг 2: Подключение статических файлов

Для подключения статических файлов в шаблонах Flask используется функция url_for, которая генерирует правильные URL для доступа к этим файлам. Например, если у вас есть CSS-файл style.css, вы можете подключить его к шаблону следующим образом.

Пример подключения CSS в шаблоне base.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
  </head>
  <body>
    <header>
      <h1>Welcome to My Flask App</h1>
    </header>
    <div class="content">
      {% block content %}{% endblock %}
    </div>
    <footer>
      <p>&copy; 2024 My Flask App</p>
    </footer>
  </body>
</html>

Здесь url_for('static', filename='css/style.css') генерирует путь к файлу style.css в папке static/css. Flask автоматически обслуживает файлы из этой директории.

Шаг 3: Работа с изображениями

Изображения также хранятся в папке static/images. Подключение изображений к шаблону осуществляется с использованием того же метода url_for.

Пример подключения изображения в шаблоне:

<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">

Код указывает на файл logo.png в папке static/images, и изображение будет отображено на странице.

Шаг 4: Подключение JavaScript

Подобно CSS и изображениям, файлы JavaScript также подключаются через url_for. JavaScript-файлы обычно хранятся в папке static/js.

Пример подключения JavaScript-файла:

<script src="{{ url_for('static', filename='js/script.js') }}"></script>

Этот код подключит файл script.js, который должен находиться в папке static/js.

Шаг 5: Пример использования статических файлов

Теперь, когда вы знаете, как подключать статические файлы, давайте создадим полный пример с использованием CSS, JavaScript и изображений.

  1. Создайте файл CSS в static/css/style.css:
  2. Создайте JavaScript-файл в static/js/script.js:
  3. **Добавьте изображение logo.png в папку static/images/.
  4. Создайте шаблон base.html с подключением всех этих файлов:
  5. Создайте маршрут и подключите шаблон в app/routes.py:

Теперь при открытии страницы по адресу http://localhost:5000, CSS будет стилизовать вашу страницу, JavaScript покажет приветственное сообщение, а логотип будет отображен в верхней части страницы.

Шаг 6: Работа с версиями статических файлов

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

Пример:

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

Flask добавит к файлу уникальную метку, например, style.css?v=123456, чтобы браузер загружал обновленную версию файла.

Практические задания

Задание 23. Создание страницы с подключением CSS и JavaScript: Создайте страницу с несколькими разделами контента, стилизованными с помощью CSS. Добавьте кнопку, нажав на которую с помощью JavaScript, будет появляться всплывающее сообщение.

Задание 24. Добавление изображений: Подключите несколько изображений на странице и организуйте галерею, используя стили CSS.

Задание 25. Работа с версиями файлов: Попробуйте обновить CSS-файл, изменив стили, и убедитесь, что браузер загружает новую версию после обновления страницы.

Аутентификация и авторизация пользователей

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

Шаг 1: Установка необходимых библиотек

Для реализации аутентификации и авторизации нам понадобятся несколько библиотек: Flask-Login для управления сессиями пользователей и Werkzeug для безопасного хэширования паролей.

Установите их через pip:

pip install flask-login

Шаг 2: Настройка Flask-Login

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

  1. Добавьте Flask-Login в app/__init__.py:
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_login import LoginManager
    from config import Config
    
    app = Flask(__name__)
    app.config.from_object(Config)
    
    db = SQLAlchemy(app)
    login = LoginManager(app)
    login.login_view = 'login'  # Страница для перенаправления неавторизованных пользователей
    
    from app import routes, models
    
    1. login.login_view указывает маршрут для перенаправления пользователей, которые пытаются получить доступ к защищенным страницам без авторизации.

Шаг 3: Создание модели пользователя

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

В файле app/models.py обновите модель пользователя:

from app import db
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from app import login

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return f'<User {self.username}>'

@login.user_loader
def load_user(id):
    return User.query.get(int(id))

Что здесь происходит:

  • UserMixin добавляет к модели функции, необходимые для работы с Flask-Login (например, is_authenticated, is_active, get_id).
  • set_password и check_password используются для хэширования паролей и проверки их корректности.
  • load_user — функция для загрузки пользователя по ID из базы данных.

Шаг 4: Регистрация пользователя

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

  1. Создайте форму регистрации в app/forms.py:
    from flask_wtf import FlaskForm
    from wtforms import StringField, PasswordField, SubmitField
    from wtforms.validators import DataRequired, Email, EqualTo, ValidationError
    from app.models import User
    
    class RegistrationForm(FlaskForm):
        username = StringField('Username', validators=[DataRequired()])
        email = StringField('Email', validators=[DataRequired(), Email()])
        password = PasswordField('Password', validators=[DataRequired()])
        password2 = PasswordField(
            'Repeat Password', validators=[DataRequired(), EqualTo('password')])
        submit = SubmitField('Register')
    
        def validate_username(self, username):
            user = User.query.filter_by(username=username.data).first()
            if user is not None:
                raise ValidationError('Please use a different username.')
    
        def validate_email(self, email):
            user = User.query.filter_by(email=email.data).first()
            if user is not None:
                raise ValidationError('Please use a different email address.')
    
  2. Создайте маршрут для регистрации в app/routes.py:
    from flask import render_template, flash, redirect, url_for
    from app import app, db
    from app.forms import RegistrationForm
    from app.models import User
    
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        form = RegistrationForm()
        if form.validate_on_submit():
            user = User(username=form.username.data, email=form.email.data)
            user.set_password(form.password.data)
            db.session.add(user)
            db.session.commit()
            flash('Congratulations, you are now a registered user!')
            return redirect(url_for('login'))
        return render_template('register.html', title='Register', form=form)
    
  3. Создайте шаблон register.html:
    {% extends "base.html" %}
    
    {% block content %}
      <h2>Register</h2>
      <form method="POST">
        {{ form.hidden_tag() }}
        <p>{{ form.username.label }}<br>{{ form.username(size=32) }}</p>
        <p>{{ form.email.label }}<br>{{ form.email(size=32) }}</p>
        <p>{{ form.password.label }}<br>{{ form.password(size=32) }}</p>
        <p>{{ form.password2.label }}<br>{{ form.password2(size=32) }}</p>
        <p>{{ form.submit() }}</p>
      </form>
    {% endblock %}
    

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

Шаг 5: Вход в систему

Теперь добавим форму для входа в систему и реализуем процесс аутентификации.

  1. Создайте форму входа в app/forms.py:
    from flask_wtf import FlaskForm
    from wtforms import StringField, PasswordField, BooleanField, SubmitField
    from wtforms.validators import DataRequired
    
    class LoginForm(FlaskForm):
        username = StringField('Username', validators=[DataRequired()])
        password = PasswordField('Password', validators=[DataRequired()])
        remember_me = BooleanField('Remember Me')
        submit = SubmitField('Sign In')
    
  2. Создайте маршрут для входа в app/routes.py:
    from flask import render_template, flash, redirect, url_for, request
    from flask_login import current_user, login_user, logout_user
    from app.models import User
    from app.forms import LoginForm
    from werkzeug.urls import url_parse
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if current_user.is_authenticated:
            return redirect(url_for('index'))
        form = LoginForm()
        if form.validate_on_submit():
            user = User.query.filter_by(username=form.username.data).first()
            if user is None or not user.check_password(form.password.data):
                flash('Invalid username or password')
                return redirect(url_for('login'))
            login_user(user, remember=form.remember_me.data)
            next_page = request.args.get('next')
            if not next_page or url_parse(next_page).netloc != '':
                next_page = url_for('index')
            return redirect(next_page)
        return render_template('login.html', title='Sign In', form=form)
    
  3. Создайте шаблон login.html:
    {% extends "base.html" %}
    
    {% block content %}
      <h2>Sign In</h2>
      <form method="POST">
        {{ form.hidden_tag() }}
        <p>{{ form.username.label }}<br>{{ form.username(size=32) }}</p>
        <p>{{ form.password.label }}<br>{{ form.password(size=32) }}</p>
        <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
        <p>{{ form.submit() }}</p>
      </form>
    {% endblock %}
    

Шаг 6: Выход из системы

Для выхода из системы нужно просто удалить сессию пользователя. Это делается с помощью функции logout_user.

  1. Добавьте маршрут для выхода в app/routes.py:
    from flask_login import logout_user
    
    @app.route('/logout')
    def logout():
        logout_user()
        return redirect(url_for('index'))
    

Шаг 7: Ограничение доступа к страницам

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

Пример использования @login_required:

from flask_login import login_required

@app.route('/profile')
@login_required
def profile():
    return render_template('profile.html', title='Profile')

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

Практические задания

Задание 26. Регистрация и вход: Реализуйте процесс регистрации и входа для пользователей. После входа пользователи должны перенаправляться на личную страницу профиля.

Задание 27. Ограничение доступа: Ограничьте доступ к странице профиля только для авторизованных пользователей. Если пользователь не авторизован, он должен быть перенаправлен на страницу входа.

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

Расширение функциональности с помощью плагинов Flask

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

Шаг 1: Что такое плагины Flask?

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

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

Шаг 2: Установка и использование плагинов

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

Пример 1: Flask-Mail — отправка электронной почты

Flask-Mail — это расширение для интеграции с почтовыми сервисами. С его помощью можно отправлять электронные письма из приложения Flask.

  1. Установка Flask-Mail:
    pip install Flask-Mail
    
  2. Настройка Flask-Mail:
    Добавьте настройки почты в файл config.py:
    class Config:
        SECRET_KEY = 'mysecretkey'
        SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
        MAIL_SERVER = 'smtp.example.com'
        MAIL_PORT = 587
        MAIL_USE_TLS = True
        MAIL_USERNAME = 'your-email@example.com'
        MAIL_PASSWORD = 'your-password'
    
  3. Инициализация Flask-Mail в приложении:
    В файле app/__init__.py инициализируйте плагин:
    from flask_mail import Mail
    
    app = Flask(__name__)
    app.config.from_object(Config)
    mail = Mail(app)
    
  4. Отправка письма:
    Создадим маршрут для отправки письма:
    from flask_mail import Message
    from app import app, mail
    from flask import render_template, flash, redirect, url_for
    
    @app.route('/send_email')
    def send_email():
        msg = Message('Hello from Flask',
                      sender='your-email@example.com',
                      recipients=['recipient@example.com'])
        msg.body = 'This is a test email sent from a Flask application!'
        mail.send(msg)
        flash('Email sent successfully!')
        return redirect(url_for('index'))
    

Теперь, когда вы откроете маршрут /send_email, Flask отправит письмо на указанный адрес.

Пример 2: Flask-Migrate — управление миграциями базы данных

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

  1. Установка Flask-Migrate:
    pip install Flask-Migrate
    
  2. Настройка Flask-Migrate:
    Инициализируйте Flask-Migrate в файле app/__init__.py:
    from flask_migrate import Migrate
    from app import app, db
    
    migrate = Migrate(app, db)
    
  3. Использование Flask-Migrate:
    Выполните инициализацию миграций (это нужно сделать один раз):
    flask db init
    

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

  • Создать миграцию после изменения моделей:
    flask db migrate -m "Описание изменений"
    
  • Применить миграции к базе данных:
    flask db upgrade
    

Пример 3: Flask-Caching — кэширование данных

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

  1. Установка Flask-Caching:
    pip install Flask-Caching
    
  2. Настройка кэширования:
    Добавьте конфигурацию кэширования в файл config.py:
    class Config:
        SECRET_KEY = 'mysecretkey'
        SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
        CACHE_TYPE = 'SimpleCache'  # Можно выбрать другие типы, например, Redis
        CACHE_DEFAULT_TIMEOUT = 300
    
  3. Инициализация Flask-Caching в приложении:
    В файле app/__init__.py инициализируйте кэш:
    from flask_caching import Cache
    
    app = Flask(__name__)
    app.config.from_object(Config)
    cache = Cache(app)
    
  4. Использование кэша:
    Кэшируем результаты функции с помощью декоратора @cache.cached:
    @app.route('/expensive_operation')
    @cache.cached(timeout=60)
    def expensive_operation():
        # Это имитация сложной операции, которую нужно кэшировать
        return "Expensive operation result"
    

Теперь результат функции будет кэшироваться на 60 секунд. При повторном обращении за это время результат будет браться из кэша, что повышает производительность.

Пример 4: Flask-Bcrypt — хэширование паролей

Flask-Bcrypt — это расширение для безопасного хэширования паролей, которое заменяет стандартные методы хэширования паролей в Flask.

  1. Установка Flask-Bcrypt:
    pip install Flask-Bcrypt
    
  2. Инициализация Flask-Bcrypt в приложении:
    from flask_bcrypt import Bcrypt
    
    app = Flask(__name__)
    bcrypt = Bcrypt(app)
    

    Добавьте инициализацию в файл app/__init__.py:

  3. Использование Flask-Bcrypt для хэширования паролей:
    Обновим методы модели User для использования Flask-Bcrypt:

    from app import bcrypt
    
    class User(db.Model):
        # остальные поля...
        password_hash = db.Column(db.String(128))
    
        def set_password(self, password):
            self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
    
        def check_password(self, password):
            return bcrypt.check_password_hash(self.password_hash, password)
    

Теперь пароли будут хэшироваться с помощью Bcrypt, обеспечивая лучшую безопасность данных пользователей.

Пример 5: Flask-Admin — административная панель

Flask-Admin — это расширение, которое добавляет к вашему приложению мощную и легко настраиваемую административную панель.

  1. Установка Flask-Admin:
  2. Настройка Flask-Admin:
    Инициализируйте Flask-Admin в файле app/__init__.py:

Теперь при переходе на URL /admin у вас будет доступ к административной панели, где можно управлять пользователями и другими моделями.

Практические задания

Задание 29. Отправка email: Настройте отправку писем в вашем проекте с использованием Flask-Mail. Реализуйте страницу для отправки писем пользователям с подтверждением отправки.

Задание 30. Кэширование данных: Используйте Flask-Caching для кэширования результата сложного вычисления или частого запроса к базе данных.

Задание 31. Административная панель: Подключите Flask-Admin и настройте административную панель для управления пользователями и постами в вашем проекте.

Развертывание Flask-приложения

Развертывание Flask-приложения — это важный этап, который делает ваше приложение доступным для пользователей в сети. Flask поставляется с встроенным сервером для разработки, но для боевой эксплуатации требуются более надежные серверы и инфраструктура. В этом разделе мы рассмотрим несколько способов развертывания Flask-приложения на популярных платформах, таких как Heroku и DigitalOcean, а также обсудим использование WSGI-серверов (например, Gunicorn) и прокси-серверов (например, Nginx).

Шаг 1: Подготовка к развертыванию

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

  1. Создайте файл requirements.txt: Это файл, содержащий список всех зависимостей вашего проекта. В нем будут указаны библиотеки, которые должны быть установлены на сервере.Создайте файл с зависимостями командой:
    pip freeze > requirements.txt
    
  2. Создайте файл Procfile (только для Heroku): Этот файл указывает Heroku, как запускать ваше приложение. В нем нужно указать команду для запуска приложения через WSGI-сервер.Пример Procfile:
    web: gunicorn run:app
    

    Здесь run:app — это указание на модуль (файл run.py) и имя экземпляра Flask-приложения (app).

  3. Добавьте файл конфигурации .env: Это файл для хранения переменных окружения, таких как секретные ключи и параметры базы данных.Пример .env:
    SECRET_KEY=mysecretkey
    DATABASE_URL=sqlite:///app.db
    

Шаг 2: Развертывание на Heroku

Heroku — это популярная платформа для хостинга веб-приложений, которая поддерживает Flask. Давайте развернем приложение на Heroku.

  1. Установка Heroku CLI:Сначала вам нужно установить Heroku CLI. Следуйте инструкциям с официального сайта Heroku CLI.
  2. Войдите в аккаунт Heroku:После установки выполните вход:
    heroku login
    
  3. Инициализация Heroku-приложения:Перейдите в директорию вашего проекта и создайте новое приложение Heroku:
    heroku create your-app-name
    

    Команда создаст новое приложение на Heroku и подключит его к вашему репозиторию Git.

  4. Добавление Gunicorn в зависимости:Flask не предназначен для работы в продакшене напрямую, поэтому мы будем использовать WSGI-сервер Gunicorn.Установите Gunicorn:
    pip install gunicorn
    

    Добавьте Gunicorn в requirements.txt:

    pip freeze > requirements.txt
    
  5. Отправка приложения на Heroku:Выполните команды для добавления изменений в репозиторий и отправки приложения на сервер:
    git add .
    git commit -m "Deploy Flask app to Heroku"
    git push heroku master
    
  6. Запуск приложения:После успешного развертывания откройте приложение в браузере:
    heroku open
    
  7. Настройка переменных окружения на Heroku:Чтобы установить переменные окружения, такие как секретные ключи или данные для подключения к базе данных, используйте команду heroku config:set:
    heroku config:set SECRET_KEY=mysecretkey
    

Шаг 3: Развертывание на DigitalOcean (или любой VPS)

DigitalOcean предоставляет возможность развернуть приложение на виртуальном частном сервере (VPS). Вот как можно настроить ваше Flask-приложение на DigitalOcean с использованием Nginx и Gunicorn.

  1. Создание VPS на DigitalOcean:Зарегистрируйтесь на DigitalOcean, создайте новый Droplet (виртуальный сервер) и подключитесь к нему через SSH.
  2. Установка Python и зависимостей:На сервере установите Python, виртуальное окружение и необходимые пакеты:
    sudo apt update
    sudo apt install python3-pip python3-venv nginx
    
  3. Клонирование вашего приложения:Скопируйте ваш проект на сервер, используя Git:
    git clone https://github.com/yourusername/your-flask-app.git
    cd your-flask-app
    
  4. Создание виртуального окружения и установка зависимостей:В директории вашего проекта создайте виртуальное окружение и установите зависимости:
    python3 -m venv venv
    source venv/bin/activate
    pip install -r requirements.txt
    
  5. Установка и настройка Gunicorn:Установите Gunicorn в виртуальное окружение:
    pip install gunicorn
    

    Запустите Gunicorn, чтобы проверить, что ваше приложение работает:

    gunicorn --bind 0.0.0.0:8000 run:app
    
  6. Настройка Nginx:Nginx будет работать как прокси-сервер перед Gunicorn. Настройте Nginx для проксирования запросов на Gunicorn.Создайте конфигурационный файл для вашего приложения:
    sudo nano /etc/nginx/sites-available/your-app
    

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

    server {
        listen 80;
        server_name your_domain_or_ip;
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    
        location /static {
            alias /path/to/your/app/static;
        }
    }
    

    Активируйте конфигурацию:

    sudo ln -s /etc/nginx/sites-available/your-app /etc/nginx/sites-enabled
    sudo systemctl restart nginx
    
  7. Запуск приложения с Gunicorn и Supervisor:Чтобы ваше приложение запускалось автоматически при перезагрузке сервера, используйте Supervisor.Установите Supervisor:
    sudo apt install supervisor
    

    Создайте конфигурационный файл для вашего приложения:

    sudo nano /etc/supervisor/conf.d/your-app.conf
    

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

    [program:your-app]
    command=/path/to/your/app/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 run:app
    directory=/path/to/your/app
    autostart=true
    autorestart=true
    stderr_logfile=/var/log/your-app.err.log
    stdout_logfile=/var/log/your-app.out.log
    

    Перезапустите Supervisor:

    sudo supervisorctl reread
    sudo supervisorctl update
    

     

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

Шаг 4: Использование Docker для развертывания

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

  1. Создайте файл Dockerfile:
    FROM python:3.9-slim
    
    WORKDIR /app
    
    COPY requirements.txt requirements.txt
    RUN pip install -r requirements.txt
    
    COPY . .
    
    CMD ["gunicorn", "--bind", "0.0.0.0:8000", "run:app"]
    
  2. Создайте файл docker-compose.yml:
    version: '3'
    services:
      web:
        build: .
        ports:
          - "8000:8000"
    
  3. Сборка и запуск контейнера:Для запуска контейнера выполните команды:
    docker-compose build
    docker-compose up
    

Теперь ваше приложение работает внутри контейнера Docker.

Практические задания

Задание 32. Развертывание на Heroku: Создайте и разверните Flask-приложение на платформе Heroku. Убедитесь, что приложение корректно работает в продакшене.

Задание 33. Развертывание на VPS: Настройте виртуальный сервер (например, на DigitalOcean) для развертывания Flask-приложения с использованием Gunicorn и Nginx.

Задание 34. Docker: Подготовьте ваше Flask-приложение для работы в контейнере Docker. Запустите его локально с использованием Docker и Docker Compose.

Тестирование Flask приложений

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

Зачем нужно тестирование?

  1. Уверенность в работе приложения: Тесты помогают проверить, что приложение работает так, как задумано.
  2. Предотвращение ошибок: Тестирование позволяет обнаруживать ошибки на ранних этапах разработки.
  3. Автоматизация: Тесты можно запускать автоматически при внесении изменений в код, что экономит время на ручные проверки.

Шаг 1: Встроенные инструменты для тестирования Flask

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

Пример использования тестового клиента

  1. Простое приложение Flask для тестирования:
    from flask import Flask, jsonify
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return "Hello, World!"
    
    @app.route('/json')
    def json_data():
        return jsonify({"message": "Hello, Flask!"})
    
    if __name__ == '__main__':
        app.run()
    
  2. Пример тестирования с использованием встроенного тестового клиента Flask:
    Создадим файл test_app.py для тестирования:
    import unittest
    from app import app
    
    class FlaskTestCase(unittest.TestCase):
    
        def setUp(self):
            # Устанавливаем тестовый клиент
            self.app = app.test_client()
            # Включаем режим тестирования
            self.app.testing = True
    
        def test_index(self):
            # Выполняем GET-запрос на главную страницу
            response = self.app.get('/')
            # Проверяем, что ответ успешен
            self.assertEqual(response.status_code, 200)
            # Проверяем содержание ответа
            self.assertIn(b'Hello, World!', response.data)
    
        def test_json(self):
            # Тестирование JSON-ответа
            response = self.app.get('/json')
            self.assertEqual(response.status_code, 200)
            # Проверяем формат ответа (должен быть JSON)
            self.assertEqual(response.content_type, 'application/json')
            # Проверяем содержание JSON
            self.assertEqual(response.get_json(), {"message": "Hello, Flask!"})
    
    if __name__ == '__main__':
        unittest.main()
    
  • setUp(): Этот метод запускается перед каждым тестом и инициализирует тестовый клиент.
  • Тестирование главной страницы: Мы отправляем GET-запрос на / и проверяем статус-код и содержание ответа.
  • Тестирование JSON-ответа: Мы отправляем GET-запрос на /json и проверяем, что ответ — это корректный JSON с правильными данными.

Запустите тесты командой:

python test_app.py

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

..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

Шаг 2: Использование pytest для тестирования Flask-приложений

pytest — это мощная и удобная библиотека для тестирования в Python. Она легко интегрируется с Flask и делает процесс написания и запуска тестов простым.

  1. Установка pytest:
    pip install pytest
    
  2. Пример тестирования с использованием pytest:
    Создадим файл test_app_pytest.py:
    import pytest
    from app import app
    
    @pytest.fixture
    def client():
        # Устанавливаем тестовый клиент для использования в тестах
        with app.test_client() as client:
            app.config['TESTING'] = True
            yield client
    
    def test_index(client):
        response = client.get('/')
        assert response.status_code == 200
        assert b'Hello, World!' in response.data
    
    def test_json(client):
        response = client.get('/json')
        assert response.status_code == 200
        assert response.is_json
        assert response.get_json() == {"message": "Hello, Flask!"}
    
  • Фикстура client: Создаем тестовый клиент Flask, который будет использоваться в каждом тесте.
  • test_index и test_json: Тесты проверяют работу двух маршрутов, как в примере с unittest.

Запустите тесты с помощью команды:

pytest

Вывод будет похож на:

============================= test session starts ==============================
collected 2 items                                                               

test_app_pytest.py ..                                                    [100%]

============================== 2 passed in 0.01s ===============================

Шаг 3: Тестирование форм и POST-запросов

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

  1. Пример маршрута с формой:
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            username = request.form['username']
            password = request.form['password']
            if username == 'admin' and password == 'secret':
                return 'Logged in successfully'
            else:
                return 'Invalid credentials', 401
        return '''
            <form method="post">
                <input type="text" name="username">
                <input type="password" name="password">
                <input type="submit" value="Login">
            </form>
        '''
    
  2. Тестирование формы:
    Создадим тест для обработки формы:
    def test_login_success(client):
        response = client.post('/login', data={
            'username': 'admin',
            'password': 'secret'
        })
        assert response.status_code == 200
        assert b'Logged in successfully' in response.data
    
    def test_login_failure(client):
        response = client.post('/login', data={
            'username': 'admin',
            'password': 'wrongpassword'
        })
        assert response.status_code == 401
        assert b'Invalid credentials' in response.data
    

Мы выполняем POST-запрос с данными формы и проверяем, как приложение реагирует на правильные и неправильные данные.

Шаг 4: Тестирование с базой данных

Для тестирования взаимодействия с базой данных можно использовать SQLite в режиме памяти (in-memory), чтобы не зависеть от реальной базы данных и очищать данные после каждого теста.

  1. Создание тестовой базы данных:
    Используем SQLite для тестирования:
    import pytest
    from app import app, db
    
    @pytest.fixture
    def client():
        app.config['TESTING'] = True
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
        with app.test_client() as client:
            with app.app_context():
                db.create_all()  # Создаем временные таблицы в базе данных
            yield client
            with app.app_context():
                db.drop_all()  # Удаляем все данные после теста
    
  2. Тестирование с использованием базы данных:
    Предположим, у нас есть модель User, и мы хотим протестировать добавление нового пользователя:
    from app.models import User
    
    def test_user_creation(client):
        with app.app_context():
            user = User(username='testuser', email='test@example.com')
            db.session.add(user)
            db.session.commit()
    
            created_user = User.query.filter_by(username='testuser').first()
            assert created_user is not None
            assert created_user.email == 'test@example.com'
    

Этот тест создает пользователя в тестовой базе данных и проверяет, что он успешно добавлен.

Практические задания

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

Задание 36. Тестирование форм: Реализуйте тесты для проверки обработки POST-запросов, особенно для форм регистрации и входа.

Задание 37. Тестирование с базой данных: Напишите тесты, которые проверяют добавление, обновление и удаление записей в базе данных, используя SQLite в режиме памяти.

Разработка API с помощью Flask

Flask часто используется для разработки API (Application Programming Interface) благодаря своей гибкости и простоте. API позволяет обмениваться данными между различными системами или компонентами приложения, предоставляя клиентам доступ к данным и функциям через HTTP-запросы. В этом разделе мы рассмотрим, как разрабатывать API с помощью Flask, как обрабатывать запросы и ответы в формате JSON, и какие расширения могут упростить разработку API.

Шаг 1: Основы API на Flask

API — это способ взаимодействия между различными приложениями или компонентами через HTTP-запросы (например, GET, POST, PUT, DELETE). Клиенты могут отправлять запросы к вашему API для выполнения операций, таких как получение данных, добавление новых записей или обновление существующих.

Flask изначально поддерживает создание API через маршруты, обрабатывающие JSON-данные.

Пример простого API на Flask:

  1. Пример создания API для работы с задачами:
    from flask import Flask, jsonify, request
    
    app = Flask(__name__)
    
    tasks = [
        {'id': 1, 'title': 'Buy groceries', 'done': False},
        {'id': 2, 'title': 'Learn Flask', 'done': False},
    ]
    
    @app.route('/api/tasks', methods=['GET'])
    def get_tasks():
        return jsonify({'tasks': tasks})
    
    @app.route('/api/tasks/<int:task_id>', methods=['GET'])
    def get_task(task_id):
        task = next((task for task in tasks if task['id'] == task_id), None)
        if task is None:
            return jsonify({'error': 'Task not found'}), 404
        return jsonify({'task': task})
    
    @app.route('/api/tasks', methods=['POST'])
    def create_task():
        if not request.json or not 'title' in request.json:
            return jsonify({'error': 'Invalid request'}), 400
        new_task = {
            'id': tasks[-1]['id'] + 1,
            'title': request.json['title'],
            'done': False
        }
        tasks.append(new_task)
        return jsonify({'task': new_task}), 201
    
    if __name__ == '__main__':
        app.run(debug=True)
    

Что здесь происходит:

  • GET /api/tasks — Возвращает список всех задач в формате JSON.
  • GET /api/tasks/int:task_id — Возвращает данные одной задачи по ее ID.
  • POST /api/tasks — Создает новую задачу, получая данные через JSON-запрос.

Шаг 2: Возвращение JSON-ответов

Для разработки API важно правильно возвращать данные в формате JSON. Flask упрощает этот процесс с помощью метода jsonify, который преобразует Python-объекты (например, словари) в формат JSON.

Пример:

from flask import jsonify

@app.route('/api/status', methods=['GET'])
def api_status():
    return jsonify({'status': 'API is running'})

Этот маршрут возвращает JSON-ответ, который будет выглядеть так:

{
  "status": "API is running"
}

Шаг 3: Обработка запросов с JSON-данными

API часто работает с JSON-данными, получаемыми через HTTP-запросы (POST, PUT и т.д.). Flask позволяет легко обрабатывать такие запросы с помощью объекта request.

Пример обработки JSON-запроса:

from flask import request

@app.route('/api/tasks', methods=['POST'])
def add_task():
    if not request.json or not 'title' in request.json:
        return jsonify({'error': 'Invalid input'}), 400
    new_task = {
        'id': tasks[-1]['id'] + 1,
        'title': request.json['title'],
        'done': False
    }
    tasks.append(new_task)
    return jsonify({'task': new_task}), 201
  • request.json: Позволяет получить JSON-данные из запроса. Если данные не являются корректным JSON, это вызовет ошибку.

Шаг 4: Обработка HTTP-методов (POST, PUT, DELETE)

Разработка API включает поддержку различных HTTP-методов, таких как POST для создания записей, PUT для обновления и DELETE для удаления.

  1. Добавление новой задачи (POST):
    @app.route('/api/tasks', methods=['POST'])
    def create_task():
        if not request.json or not 'title' in request.json:
            return jsonify({'error': 'Invalid request'}), 400
        new_task = {
            'id': tasks[-1]['id'] + 1,
            'title': request.json['title'],
            'done': False
        }
        tasks.append(new_task)
        return jsonify({'task': new_task}), 201
    
  2. Обновление задачи (PUT):
    @app.route('/api/tasks/<int:task_id>', methods=['PUT'])
    def update_task(task_id):
        task = next((task for task in tasks if task['id'] == task_id), None)
        if task is None:
            return jsonify({'error': 'Task not found'}), 404
        if not request.json:
            return jsonify({'error': 'Invalid request'}), 400
        task['title'] = request.json.get('title', task['title'])
        task['done'] = request.json.get('done', task['done'])
        return jsonify({'task': task})
    
  3. Удаление задачи (DELETE):
    @app.route('/api/tasks/<int:task_id>', methods=['DELETE'])
    def delete_task(task_id):
        task = next((task for task in tasks if task['id'] == task_id), None)
        if task is None:
            return jsonify({'error': 'Task not found'}), 404
        tasks.remove(task)
        return jsonify({'result': 'Task deleted'})
    

Шаг 5: Обработка ошибок

Важно правильно обрабатывать ошибки в API и возвращать соответствующие коды ответов HTTP. Например, если пользователь пытается получить несуществующую задачу, следует вернуть код 404 (не найдено).

Пример обработки ошибки 404:

@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Not found'}), 404

Теперь, если пользователь запрашивает несуществующий маршрут, он получит JSON-ответ с ошибкой.

Шаг 6: Flask-RESTful — упрощение разработки API

Flask-RESTful — это расширение для Flask, которое упрощает разработку API. Оно добавляет классы и методы для работы с ресурсами, что делает код API более организованным и читаемым.

  1. Установка Flask-RESTful:
    pip install flask-restful
    
  2. Пример использования Flask-RESTful:
    from flask import Flask, request
    from flask_restful import Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    tasks = [
        {'id': 1, 'title': 'Buy groceries', 'done': False},
        {'id': 2, 'title': 'Learn Flask', 'done': False},
    ]
    
    class TaskListAPI(Resource):
        def get(self):
            return {'tasks': tasks}
    
        def post(self):
            if not request.json or not 'title' in request.json:
                return {'error': 'Invalid request'}, 400
            new_task = {
                'id': tasks[-1]['id'] + 1,
                'title': request.json['title'],
                'done': False
            }
            tasks.append(new_task)
            return {'task': new_task}, 201
    
    class TaskAPI(Resource):
        def get(self, task_id):
            task = next((task for task in tasks if task['id'] == task_id), None)
            if task is None:
                return {'error': 'Task not found'}, 404
            return {'task': task}
    
        def put(self, task_id):
            task = next((task for task in tasks if task['id'] == task_id), None)
            if task is None:
                return {'error': 'Task not found'}, 404
            task['title'] = request.json.get('title', task['title'])
            task['done'] = request.json.get('done', task['done'])
            return {'task': task}
    
        def delete(self, task_id):
            task = next((task for task in tasks if task['id'] == task_id), None)
            if task is None:
                return {'error': 'Task not found'}, 404
            tasks.remove(task)
            return {'result': 'Task deleted'}
    
    api.add_resource(TaskListAPI, '/api/tasks')
    api.add_resource(TaskAPI, '/api/tasks/<int:task_id>')
    
    if __name__ == '__main__':
        app.run(debug=True)
    
  • Resource: Представляет API-ресурс. Методы get, post, put и delete связаны с различными HTTP-методами.
  • add_resource: Регистрирует ресурс в API с определенным маршрутом.

Шаг 7. Тестирование API

Основные инструменты для тестирования Flask

Для тестирования Flask-приложений в Python часто используются:

  • unittest — стандартная библиотека Python для написания тестов.
  • pytest — более гибкая и удобная библиотека, подходящая как для простых, так и сложных проектов.
  • Flask’s testing client — специальный тестовый клиент, встроенный в Flask, который имитирует запросы и позволяет проверять ответы от API.

Пример теста для Flask-приложения

Давайте посмотрим, как это выглядит на практике. Пример простого приложения Flask:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/greet/<name>', methods=['GET'])
def greet(name):
    return jsonify({"message": f"Hello, {name}!"})

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

Теперь создадим тест для этого API с помощью unittest:

import unittest
from app import app

class APITestCase(unittest.TestCase):

    def setUp(self):
        self.app = app.test_client()
        self.app.testing = True

    def test_greet(self):
        response = self.app.get('/api/greet/John')
        data = response.get_json()
        self.assertEqual(response.status_code, 200)
        self.assertEqual(data['message'], 'Hello, John!')

if __name__ == '__main__':
    unittest.main()

Здесь тест проверяет, что:

  • API возвращает правильный статус код (200).
  • Ответ содержит правильное сообщение.

Практические задания:

Задание 38. Напишите небольшое API с несколькими маршрутами: например, для получения списка товаров и добавления товара в корзину.

Задание 39. Создайте тесты для вашего API. Проверяйте как успешные, так и ошибочные запросы (например, если товар не найден).

Поддержка WebSocket и работа в реальном времени с Flask

Сегодня пользователи ожидают мгновенной реакции от приложений. Это особенно важно для таких сервисов, как чаты, онлайн-игры или биржевые платформы, где информация должна обновляться мгновенно. Чтобы реализовать такие функции, вместо стандартных HTTP-запросов можно использовать WebSocket.

Что такое WebSocket?

HTTP-запросы работают по принципу «запрос-ответ» — клиент отправляет запрос, сервер отвечает, и соединение закрывается. Но что, если нам нужно постоянное соединение? Чтобы сервер мог сам отправлять данные, как только они обновились? Тут на помощь приходит WebSocket — технология, позволяющая поддерживать постоянное соединение между клиентом и сервером.

Пример из жизни: представьте себе приложение для отслеживания курсов валют. Без WebSocket пользователю пришлось бы постоянно обновлять страницу, чтобы увидеть актуальные данные. С WebSocket же сервер сам отправит обновления, как только курс валюты изменится.

Flask и WebSocket

Flask не поддерживает WebSocket «из коробки», но для этого есть сторонняя библиотека Flask-SocketIO, которая значительно упрощает работу с реальными временем.

Пример простого WebSocket-сервера на Flask:

from flask import Flask, render_template
from flask_socketio import SocketIO, send

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

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

@socketio.on('message')
def handle_message(message):
    print(f'Received message: {message}')
    send(f'Echo: {message}', broadcast=True)

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

Это простой пример чата, где каждый пользователь может отправить сообщение, и сервер его «отразит» всем подключенным клиентам.

Как это работает:

  1. Клиент открывает соединение с сервером через WebSocket.
  2. Когда клиент отправляет сообщение, сервер его получает и сразу отправляет обратно всем клиентам.
  3. Клиенты моментально видят новое сообщение, не обновляя страницу.

HTML-клиент для WebSocket:

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Chat</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <script type="text/javascript">
        var socket = io();

        socket.on('connect', function() {
            console.log('Connected to WebSocket');
        });

        socket.on('message', function(msg) {
            document.getElementById('messages').innerHTML += '<p>' + msg + '</p>';
        });

        function sendMessage() {
            var message = document.getElementById('message').value;
            socket.send(message);
        }
    </script>
</head>
<body>
    <h1>WebSocket Chat</h1>
    <input type="text" id="message" placeholder="Enter your message">
    <button onclick="sendMessage()">Send</button>
    <div id="messages"></div>
</body>
</html>

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

Практические задания:

Задание 40. Создайте простой чат с использованием Flask-SocketIO, где пользователи могут общаться в реальном времени.

Задание 41. Добавьте уведомления о подключении новых пользователей в чат.

Задание 42. Реализуйте возможность отправки личных сообщений.

Проектные задания

Простая сложность

1. Создание блога с функциями аутентификации и админ-панелью

Описание проекта:
Студенты разрабатывают веб-приложение для ведения блога, где пользователи могут регистрироваться, создавать, редактировать и удалять статьи. Администраторы имеют доступ к дополнительным функциям, таким как модерация и управление пользователями.

Ключевые задачи:

  • Настройка Flask-проекта с базовой структурой.
  • Работа с базой данных (например, SQLite или PostgreSQL) для хранения пользователей и постов.
  • Аутентификация и авторизация пользователей (регистрация, логин, доступ к админ-панели).
  • Создание и настройка маршрутов для CRUD операций над статьями (создание, чтение, обновление, удаление).
  • Валидация форм (например, для создания постов) и работа с шаблонами Jinja2.
  • Обработка и хранение изображений (статические файлы).

Дополнительно:

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

2. Разработка API для интернет-магазина с авторизацией и обработкой заказов

Описание проекта:
Студенты создают RESTful API для онлайн-магазина, который поддерживает авторизацию, просмотр и создание товаров, а также оформление и управление заказами.

Ключевые задачи:

  • Настройка API на Flask с использованием Flask-RESTful или Flask-Swagger для генерации документации.
  • Подключение базы данных для хранения товаров, пользователей и заказов.
  • Реализация регистрации и авторизации пользователей через JWT.
  • Проектирование маршрутов для управления товарами (просмотр списка товаров, деталей, добавление и удаление из корзины).
  • Создание API для оформления заказа и его дальнейшей обработки (например, статус заказа).
  • Тестирование API с помощью unittest или pytest для проверки корректности работы.

Дополнительно:

  • Разработка панели администратора для управления товарами и заказами.
  • Добавление системы уведомлений (например, email о статусе заказа).

3. Создание чата с поддержкой WebSocket для общения в реальном времени

Описание проекта:
В этом проекте студенты реализуют чат-приложение с использованием WebSocket для обмена сообщениями в реальном времени. Приложение может поддерживать публичные и приватные чаты.

Ключевые задачи:

  • Настройка проекта Flask с использованием Flask-SocketIO для реализации WebSocket соединений.
  • Аутентификация пользователей и управление их сессиями (регистрация и логин).
  • Создание интерфейса для обмена сообщениями в реальном времени с помощью HTML и JavaScript.
  • Реализация публичных комнат для чатов, где могут участвовать несколько пользователей.
  • Добавление личных сообщений между пользователями (приватные чаты).
  • Хранение истории сообщений в базе данных и возможность её просмотра.

Дополнительно:

  • Добавление уведомлений о входе новых пользователей или выходе из чата.
  • Создание интеграции с другими внешними сервисами (например, push-уведомления через Web Push API).

Повышенная сложность

4. Интерактивный туристический портал о России

Описание проекта:
Студенты создают туристический портал, где пользователи могут находить информацию о достопримечательностях России, прокладывать маршруты и оставлять отзывы о посещённых местах.

Ключевые задачи:

  • Настройка Flask-приложения с базовой архитектурой.
  • Создание базы данных для хранения информации о достопримечательностях, маршрутах и отзывах пользователей.
  • Разработка интерфейса для поиска по городам и регионам с отображением популярных туристических мест.
  • Реализация системы регистрации и аутентификации для пользователей, позволяющая добавлять отзывы и фотографии к достопримечательностям.
  • Интеграция с картами (например, Google Maps или Yandex Maps) для построения маршрутов между объектами.
  • Работа с шаблонами Jinja2 для динамического отображения данных (например, список достопримечательностей на главной странице).

Дополнительно:

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

5. Портал биржевой аналитики с поддержкой WebSocket

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

Ключевые задачи:

  • Настройка проекта Flask и интеграция WebSocket с помощью Flask-SocketIO.
  • Создание и подключение базы данных для хранения исторических данных по котировкам акций, облигаций и других финансовых инструментов.
  • Разработка интерфейса для отображения текущих котировок и графиков цен (с использованием библиотек для построения графиков, например, Plotly или Chart.js).
  • Реализация системы обновлений в реальном времени с использованием WebSocket, чтобы клиенты получали актуальные данные без перезагрузки страницы.
  • Аутентификация пользователей и предоставление возможности подписки на различные финансовые инструменты, чтобы получать уведомления о важных изменениях.

Дополнительно:

  • Возможность настройки оповещений по email или SMS о достижении определённых ценовых уровней.
  • Интеграция аналитики (например, прогнозов изменения цен) и уведомления о важных биржевых событиях.
  • Добавление личных кабинетов пользователей с возможностью создания портфелей и анализа их доходности в режиме реального времени.

 

Понравилась статья? Поделиться с друзьями:
Школа Виктора Комлева
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.