Share This
Связаться со мной
Крути в низ
Categories
//Разработка и создание веб-приложения на Python и Flask с базой данных

Разработка и создание веб-приложения на Python и Flask с базой данных

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

razrabotka i sozdanie veb prilozhenija na python i flask s bazoj dannyh a4f9b1a - Разработка и создание веб-приложения на Python и Flask с базой данных

Первая часть 🐍📚 Создаем аналог LiveLib.ru на Flask. Часть 1: основы работы с SQLAlchemy

Третий этап

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

Валидация форм WTForms

Для получения пользовательских данных со стороны фронтенда в Flask-приложениях обычно используют формы WTForms. Эти формы «из коробки» предоставляют отличные опции для валидации введенных данных. Расширение функциональности форм за счет макросов и дополнительных валидаторов тоже не вызывает никаких сложностей, как мы это увидим позже.

Для взаимодействия с приложением нам потребуются две формы – BookForm и UpdateBook. Первая отвечает за создание новой карточки, вторая – за редактирование существующей записи. Обложки книг можно загружать как во время создания карточки, так и после – в процессе редактирования. Обратите внимание на формат использования валидаторов:

  1. DataRequired – не даст отправить форму, если поле останется незаполненным.
  2. Length(min=5, max=100) – минимальная длина строки в поле – 5 символов, максимальная – 100.
  3. FileAllowed([‘jpg’, ‘png’]) – разрешает присоединять к форме только изображения .jpg и .png.
  4. NumberRange(min=1, max=5)]) – обеспечивает ввод оценки для книги в диапазоне от 1 до 5 включительно.

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

/reader/forms.py

         def validate_title(self, title):     	title = Book.query.filter_by(title=title.data).first()     	if title:             raise ValidationError('Такая книга уже есть в списке прочитанных.')     

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

razrabotka i sozdanie veb prilozhenija na python i flask s bazoj dannyh 0718c49 - Разработка и создание веб-приложения на Python и Flask с базой данных

Название книги оказалось неуникальным

Эта ошибка связана с атрибутом unique=True столбца title и ее проще предотвратить, чем обрабатывать. Но обработку мы тоже рассмотрим ниже – она будет реализована в функции представления.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» Интересно, перейти к каналу

Шаблоны для форм

Формам для создания и редактирования записей нужны шаблоны. Здесь есть код для create.html и edit.html. Шаблон create.html предусматривает вывод ошибок:

create.html 

         {% if form.genre.errors %}  {% for error in form.genre.errors %}    <span class="text-danger">{{ error }}</span></br>  {% endfor %} {% endif %}     

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

  • class=»form-control-label» – однострочное текстовое поле;
  • class=»form-control-textarea» – многострочное текстовое поле;
  • class=»form-control-number» – ввод цифр;
  • class=»form-control-file» – выбор файла.

В шаблоне edit.html используется предварительное заполнение формы, уже внесенной в карточку информацией, – чтобы пользователю было проще ее редактировать. Эту информацию отправляет в форму функция edit, которую мы рассмотрим чуть позже. Для генерации шаблона нужен вспомогательный макрос – он находится в файле formhelpers.html. Этот файл необходимо поместить в папку /templates вместе с обычными шаблонами. В шаблоне для редактирования есть блок для вывода сообщений об ошибках базы данных, получаемых от Flask и SQLAlchemy (остальные ошибки валидации обрабатывает WTForms):

edit.html 

         {% with messages = get_flashed_messages() %}   {% if messages %} 	{% for message in messages %}   	<span class="text-danger">{{ message }}</span> 	{% endfor %}   {% endif %} {% endwith %}      

Примечание: для создания и редактирования записей при желании можно использовать один и тот же шаблон. Если вставить в шаблон edit.html код из create.html, он точно так же получит существующие данные из функции представления. Кроме того, часто для редактирования и создания записей используют один и тот же код в одном и том же файле. В данном случае мы используем рендеринг формы редактирования с помощью макроса из _formhelpers.html просто в образовательных целях.

Обработка данных из форм

Для создания, редактирования и удаления записей нам нужно написать соответствующие функции в файле routes.py. Кроме того, нужно сделать функцию для безопасной загрузки изображений. Начнем с импорта форм и модуля Pillow, который обеспечит автоматическое сжатие обложек и сохранение файлов в нужную папку uploads:

routes.py

         from reader.forms import BookForm, UpdateBook from PIL import Image      

Фласку тоже потребуется импорт дополнительных модулей – flash, url_for и redirect. Pillow нужны модули os и secrets, а для обработки ошибок базы понадобится IntegrityError.

Функция для обработки и сохранения обложек книг выглядит так:

routes.py

         def save_picture(cover): 	random_hex = secrets.token_hex(8) 	_, f_ext = os.path.splitext(cover.filename) 	picture_fn = random_hex + f_ext 	picture_path = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], picture_fn) 	output_size = (220, 340) 	i = Image.open(cover) 	i.thumbnail(output_size) 	i.save(picture_path)  	return picture_fn      

Функция создания новых записей выглядит так:

routes.py

         @app.route('/create/', methods=('GET', 'POST')) def create():     form = BookForm()     if form.validate_on_submit():         if form.cover.data:             cover = save_picture(form.cover.data)         else:             cover ='default.jpg'            title = form.title.data         author = form.author.data         genre = form.genre.data         rating = int(form.rating.data)         description = form.description.data         notes = form.notes.data         book = Book(title=title,             author=author,             genre=genre,             rating=rating,             cover=cover,             description=description,             notes=notes)         db.session.add(book)         db.session.commit()         return redirect(url_for('index'))      return render_template('create.html', form=form)     

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

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

routes.py

         @app.route('/<int:book_id>/edit/', methods=('GET', 'POST')) def edit(book_id):     book = Book.query.get_or_404(book_id)     form = UpdateBook()     if form.validate_on_submit():         if form.cover.data:             cover = save_picture(form.cover.data)         else:             cover = book.cover         book.title = form.title.data         book.author = form.author.data         book.genre = form.genre.data         book.rating = int(form.rating.data)         book.description = form.description.data         book.notes = form.notes.data         try:             db.session.commit()             return redirect(url_for('index'))         except IntegrityError:             db.session.rollback()             flash('Произошла ошибка: такая книга уже есть в базе', 'error')             return render_template('edit.html', form=form)     

Удалить карточку книги проще простого:

routes.py

         @app.post('/<int:book_id>/delete/') def delete(book_id): 	book = Book.query.get_or_404(book_id) 	db.session.delete(book) 	db.session.commit() 	return redirect(url_for('index'))       

Для подтверждения удаления карточки будет использоваться всплывающее окно – код для этого уже есть в шаблонах. Осталось добавить в base.html, index.html, book.html, best.html и thrillers.html ссылки на соответствующие операции по созданию, редактированию и удалению карточек:

         {{ url_for('create') }} {{ url_for('edit', book_id=book.id) }} {{ url_for('delete', book_id=book.id) }}      

Теперь карточки можно удалять:

razrabotka i sozdanie veb prilozhenija na python i flask s bazoj dannyh 8af84cc - Разработка и создание веб-приложения на Python и Flask с базой данных

Удаляем Достоевского

Редактировать:

razrabotka i sozdanie veb prilozhenija na python i flask s bazoj dannyh 971cf1e - Разработка и создание веб-приложения на Python и Flask с базой данных

Редактируем информацию

И создавать:

razrabotka i sozdanie veb prilozhenija na python i flask s bazoj dannyh e0164d2 - Разработка и создание веб-приложения на Python и Flask с базой данных

Создаем новую запись

Экспорт данных из базы

Информацию из наполненной базы данных можно экспортировать самыми разными способами. Мы рассмотрим простой и практичный метод, который не требует установки никаких дополнительных модулей. Файл json, который мы использовали в первой части туториала, был создан именно таким способом.

Для реализации метода нужно внести небольшое дополнение в файл моделей /reader/models.py и написать функцию для /reader/routes.py. Сначала дополним класс Book:

/reader/models.py 

         from dataclasses import dataclass @dataclass class Book(db.Model): 	id: int 	title: str 	author: str 	genre: str 	cover: str 	rating: int 	description: str 	notes: str 	created_at: str      

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

Добавим импорт jsonify и функцию экспорта в /reader/routes.py:

/reader/routes.py

         @app.route('/export/') def data():   data = Book.query.all()   return jsonify(data)       

В файл __init__.py добавим параметр app.config['JSON_AS_ASCII'] = False – иначе jsonify вместо кириллицы экспортирует абракадабру.

Все готово: если перейти по адресу http://localhost:8000/export/, можно увидеть все содержимое базы в виде словаря:

razrabotka i sozdanie veb prilozhenija na python i flask s bazoj dannyh fd8adbf - Разработка и создание веб-приложения на Python и Flask с базой данных

Данные экспортированы

На этом работа над приложением закончена. Очевидно, что функциональность SQLAlchemy значительно упростила процесс разработки. И хотя SQLAlchemy – не единственная библиотека, предоставляющая Flask-приложениям все преимущества ORM, ее уверенно можно назвать самой понятной и гибкой, чем и объясняется ее популярность. Напоминаем, что финальная версия кода находится здесь.

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

  • 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages
  • 🐍🥤 Flask за час. Часть 2: завершаем разработку и размещаем сайт на GitHub Pages
  • 🐍🗄️ Управление данными с помощью Python, SQLite и SQLAlchemy

  • 3 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

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

Свежие комментарии

    Рубрики

    About Author 01.

    blank
    Roman Spiridonov

    Моя специальность - Back-end Developer, Software Engineer Python. Мне 39 лет, я работаю в области информационных технологий более 5 лет. Опыт программирования на Python более 3 лет. На Django более 2 лет.

    Categories 05.

    © Speccy 2022 / All rights reserved

    Связаться со мной
    Close