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