Вопрос или проблема
Я запускал приложение Flask, используя Python 3.9.13, Flask 1.1.2, Jinja2 2.11.3 и MarkupSafe 1.1.1, используя Redis (3.5.3) в качестве хранилища сессий. Когда я создавал всплывающее сообщение с помощью
flash(Markup('<b>Это</b> <b>ПРЕДУПРЕЖДЕНИЕ</b>.'),category="warning")
Flask отображал (как и ожидалось)
Это <b>ПРЕДУПРЕЖДЕНИЕ</b>.
с выбранным фоновым цветом, связанным с предупреждающими всплывающими сообщениями.
Я пытаюсь перенести приложение на Python 3.12.4, Flask 3.0.3 и MarkupSafe 2.1.5 (Jinja2 на 3.1.4, но Flask начал использовать MarkupSafe непосредственно, вместо импорта его из Jinja2, так что не думаю, что это имеет значение, Redis на 5.0.8). Однако, когда я пытаюсь перенаправить на страницу с этим установленным как всплывающее сообщение, приложение завершает работу тихо с ошибкой внутреннего сервера.
Глубже изучив Flask, я вижу, что возникает исключение Encoding objects of type Markup is unsupported
, когда он обрабатывает process_response и пытается сохранить сессию https://github.com/pallets/flask/blob/main/src/flask/app.py#L1301, и, похоже, это связано с объектом Markup в списке _flashes. Если я преобразую Markup в строку, это не приводит к сбою, но сообщение отображается как:
<b>Это</b> <b>ПРЕДУПРЕЖДЕНИЕ</b>.
(Хотя оно отображается с правильным фоновым цветом.)
Есть ли способ отформатировать всплывающее сообщение в текущем Flask так, как мы могли это делать в предыдущих версиях, или я что-то делаю не так?
Ответ или решение
Ваша ситуация с использованием flash-сообщений в Flask очень распространена, особенно после обновления до более новых версий. Рассмотрим, как корректно отображать flash-сообщения с использованием HTML-разметки, чтобы избежать ошибок, связанных с бинарным кодированием, которые возникли в более новых версиях Flask и MarkupSafe.
Проблема
Как вы уже заметили, при использовании Markup
для создания flash-сообщений возникает ошибка Encoding objects of type Markup is unsupported
. Это связано с тем, что Flask теперь не может сохранить объекты типа Markup
в сессии, так как они не поддерживают сериализацию.
Решение
Для решения этой проблемы можно воспользоваться следующими подходами:
-
Используйте обычные строки: Вместо того чтобы сохранять
Markup
, просто сохраните строку и используйте| safe
в шаблоне. Это позволят вам избежать проблем с сериализацией.Чтобы сделать это, следует изменить ваш код следующим образом:
from flask import flash flash('<b>This</b> is a <b>WARNING</b>.', category="warning")
Затем, в вашем шаблоне используйте
| safe
для отображения содержимого flash-сообщения:{% with messages = get_flashed_messages(with_categories=True) %} {% if messages %} {% for category, message in messages %} <div class="alert alert-{{ category }}"> {{ message|safe }} </div> {% endfor %} {% endif %} {% endwith %}
-
Создайте свой класс Flash-сообщения: Если вам нужно использовать
Markup
, вы можете создать простую обертку, чтобы сериализовать и десериализоватьMarkup
в строке.Вот пример:
from flask import flash, session from markupsafe import Markup class FlashMessage: def __init__(self, message, category): self.message = message self.category = category def __str__(self): return str(self.message) def flash_markup(message, category="message"): flash(FlashMessage(Markup(message), category)) # Используйте flash_markup для добавления flash-сообщений flash_markup('<b>This</b> is a <b>WARNING</b>.', category="warning")
В шаблоне вы сможете обработать его аналогично первому методу (не забыв использовать
| safe
).
Итог
Таким образом, для корректного отображения flash-сообщений с HTML-разметкой в текущих версиях Flask, вы должны использовать строки и предоставлять Flask возможность обрабатывать их с помощью | safe
в шаблонах. Это избавит вас от проблем с сериализацией и обеспечит правильное отображение flash-сообщений.