🐍📚 Создаем аналог LiveLib.ru на Flask. Часть 1: основы работы с SQLAlchemy
Изучаем взаимодействие Flask с SQLAlchemy и WTForms, создавая веб-приложение — лайт-версию сервиса LiveLib.ru — для хранения информации о прочитанных книгах. Реализуем CRUD, пагинацию, фильтры и экспорт данных.
Любое более-менее серьезное веб-приложение использует базу данных для хранения полученной от фронтенда информации. Для упрощения взаимодействия Flask-приложений с базой чаще всего используют библиотеку SQLAlchemy, а для получения и валидации данных пользователя – формы WTForms.
Обзор проекта
Это приложение для ведения списка прочитанных книг. Для каждой книги создается отдельная карточка с постером, именем автора, названием жанра, описанием сюжета, оценкой и примечаниями. Карточки можно редактировать и удалять. Весь код проекта находится здесь.
Что мы изучим в процессе работы
Узнаем, как создать базу данных и заполнить ее тестовыми данными из json-файла.
Реализуем пагинацию и набор CRUD-операций для работы с карточками книг.
Напишем пользовательский валидатор и обработаем ошибку IntegrityError.
Сделаем несколько фильтров для обработки определенных категорий данных.
Добавим в приложение простую (без JS) систему оценки книг.
Рассмотрим способы работы с объектами данных в шаблонизаторе Jinja2.
Первый этап
Прежде всего создадим директорию для проекта, активируем виртуальное окружение и установим все необходимые зависимости с помощью менеджера pipenv:
Структура готового приложения выглядит так:
Приступаем к работе
Приведенный ниже код отвечает за создание экземпляра Flask-приложения и объекта базы данных. Сохраните его в файле /reader/__init__.py:
Примечание: если вы планируете использовать другой тип базы данных – MySQL или PostgreSQL – URI должен выглядеть так:
Теперь нужно создать модель (таблицу) в базе данных для хранения информации о книгах. Для этого сохраните приведенный ниже код в файле /reader/models.py:
Значение cover по умолчанию равно default.jpg – это изображение надо заранее поместить в папку /reader/uploads/. Обратите внимание на один из атрибутов поля title, unique=True: это означает, что название книги должно быть уникальным. Если не предотвратить ввод дубликата (мы сделаем это позже во время валидации формы), работа приложения будет прервана ошибкой IntegrityError UNIQUE constraint failed.
Для запуска приложения создайте файл run.py:
Все готово для создания базы данных – мы сделаем это в интерактивной консоли Flask:
Загляните в папку /reader – там появился файл базы, database.
Примечание: после создания таблицы ее структуру нельзя просто так изменить (добавить новый столбец, к примеру).Выход – воспользоваться расширением Flask-Migrate, либо, если данных в базе совсем мало и потерять их не жаль, выполнить:
Заполнение базы тестовыми данными
Интерактивная консоль позволяет добавлять записи в базу по одной:
Или по нескольку сразу:
Если сейчас выполнить запрос к базе, можно увидеть, что все три записи благополучно добавлены:
И первый, и второй способы добавления записей в базу, очевидно, занимают слишком много времени. Поэтому проще наполнить базу информацией из файла books.json:
В результате в базу было добавлено 7 новых записей:
Основные маршруты и шаблоны
После наполнения базы можно приступать к функциям представления и шаблонам для вывода карточек. Сначала займемся маршрутом для главной страницы. Сохраните этот код в файле /reader/routes.py:
Вторая функция обеспечивает отправку изображений (обложек книг) из директории /reader/uploads. Использование этой функции необходимо потому, что по умолчанию Flask ищет изображения только в директории static (и вложенных в нее папках). Указание на пользовательскую папку для загрузки изображений нужно добавить в __init__.py:
Также в __init__.py надо сделать импорт модели и маршрутов:
Кроме того, для вывода записей нужны два шаблона – base.html и index.html, а также файл со стилями CSS. Поместите их, соответственно, в папки /reader/templates/ и /reader/static/css. Все готово – можно запускать приложение:
Пока что приложение выглядит так:
Оценка книги
Для вывода оценки книги используется простейший код в шаблоне, который печатает количество звездочек, соответствующее оценке в базе:
Чтобы просматривать карточки книг, нужно сделать новый шаблон book.html, поместить файл illustration.jpg в /reader/uploads/ и добавить необходимый маршрут в routes.py:
Кроме того, нужно добавить необходимую динамическую ссылку в шаблон index.html:
Карточка книги выглядит так:
Фильтры и работа с объектами данных
SQLAlchemy делает фильтрацию данных простейшим делом. К примеру, вот так можно обеспечить вывод карточек книг в соответствии с датой добавления:
Так же просто можно отобрать книги по определенному автору или жанру. Сделаем выборку по жанру «триллер»:
И по максимальной оценке 5:
Вставьте эти функции в /readers/routes.py и добавьте в папку templates шаблоны для вывода триллеров и лучших фильмов. В шаблон base.html нужно добавить ссылки для кнопок в верхнем меню:
Теперь можно посмотреть на выборку по триллерам:
И по лучшим книгам:
Обратите внимание: шаблонизатору Jinja2 не требуются никакие дополнительные ухищрения для работы с объектом данных, созданным в результате фильтрации: загрузка изображений и перенаправление на карточку книги не вызывают никаких проблем:
Кроме того, к атрибутам объекта данных можно применять фильтры Jinja2. Этот фильтр обеспечивает вывод даты добавления книги в формате день-месяц-год:
По умолчанию же (без фильтра) дата будет выглядеть так:
Пагинация
Последнее, что мы сделаем на этом этапе – постраничный вывод карточек. Реализовать пагинацию с помощью SQLAlchemy действительно просто. В начале файла /reader/routes.py необходимо добавить импорт request, а затем изменить функцию представления для index таким образом:
В шаблон index.html нужно внести всего 2 дополнения – изменить books на books.items:
И добавить вывод номеров страниц в самом низу:
Все готово:
В шаблоны best.html и thrillers.html тоже нужно добавить пагинацию. Для этого необходимо внести изменения сначала в их функции представления, а потом и в сами шаблоны.
Функция для best.html выглядит так:
А для thrillers.html – так:
Дополнения в самих шаблонах аналогичны тем, что мы уже сделали в index.html – нужно изменить books на books.items и добавить блок вывода пагинации:
Весь код и тестовый контент, созданные на этом этапе, можно взять здесь. В следующей, заключительной части мы реализуем загрузку и автоматическое сжатие изображений, сделаем CRUD-операции и добавим возможность экспорта контента в json-формате.
Комментарии