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

Оглавление

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

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

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

Ссылки на GitHub для этой главы: ОбзорZipDiff.

CSS фреймворки

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

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

Представляем Bootstrap

Одним из самых популярных CSS-фреймворков является Bootstrap. Если вы хотите увидеть, какие страницы можно создавать с помощью этого фреймворка, в документации есть несколько примеров.

Вот некоторые преимущества, которые вы получаете при использовании Bootstrap для оформления ваших веб-страниц:

  • Аналогично выглядит во всех основных веб-браузерах

  • Настройка размеров для экранов настольных компьютеров, планшетов и телефонов

  • Настраиваемые макеты

  • Красиво оформленные панели навигации, формы, кнопки, оповещения, всплывающие окна и т.д.

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

Как и большинство проектов с открытым исходным кодом, Bootstrap постоянно развивается. Оригинальная версия мега-учебника Flask была создана для Bootstrap 3. Редакция, которую вы сейчас читаете, создана для Bootstrap 5.3. Текущий подход к интеграции Bootstrap является довольно общим и может быть адаптирован к более новым версиям Bootstrap.

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

Первым шагом в интеграции Bootstrap с Microblog является добавление его файлов CSS и JavaScript в базовый шаблон. На странице быстрого запуска Bootstrap в качестве примера приведена короткая, но полная HTML-страница, которую я копирую ниже для вашего удобства:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
    <link
        href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
        rel="stylesheet"
        integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
        crossorigin="anonymous">
  </head>
  <body>
    <h1>Hello, world!</h1>
    <script
        src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
        crossorigin="anonymous">
    </script>
  </body>
</html>

Подход, который я могу применить, чтобы объединить это с моим шаблоном base.html, заключается в том, чтобы использовать приведенный выше в качестве нового базового шаблона, заменив теги <title> и <h1> заголовком и основным содержимым исходного базового шаблона соответственно.

Следующий шаг - заменить базовую панель навигации на более удобную из Bootstrap. На странице документации по панели навигации Bootstrap вверху показан хороший пример. Используя этот пример в качестве руководства, я создал панель навигации со ссылками "Index", "Explore", "Profile", "Login" и "Logout" из микроблога. Для удобства я настроил профиль, а также ссылки для входа и выхода так, чтобы они отображались в крайнем правом углу.

При использовании Bootstrap полезно знать о некоторых базовых примитивах компоновки. Одним из наиболее важных является контейнер, который определяет область содержимого страницы. Два основных контейнера называются container и container-fluid. В первом случае страница настраивается на использование одной из пяти предопределенных ширин страницы и центрирует содержимое в окне браузера. С другой стороны обтекающий контейнер дает вам доступ ко всей ширине страницы. Для этого приложения я решил использовать контейнер по умолчанию, потому что он предотвращает слишком широкое расширение страницы независимо от размера экрана, поэтому часть содержимого страницы будет заключена в один из этих контейнеров следующим образом:

<div class="container">
    ... page contents here ...
</div>

Последняя часть HTML-разметки в шаблоне base.html, которую необходимо адаптировать, - это раздел, отображающий отображаемые сообщения. Компонент Alert от Bootstrap прекрасно подходит для этой задачи.

Вы можете получить полностью переработанный шаблон base.html из репозитория Github для этой главы. Ниже вы можете увидеть упрощенную структуру, если хотите иметь представление о том, как она выглядит.:

app/templates/base.html: Переработанный базовый шаблон.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {% if title %}
    <title>{{ title }} - Microblog</title>
    {% else %}
    <title>Welcome to Microblog</title>
    {% endif %}
    <link ... bootstrap CSS ...>
  </head>
  <body>
    <nav>
      ... navigation bar (see complete code on GitHub) ...
    </nav>
    <div class="container mt-3">
      {% with messages = get_flashed_messages() %}
      {% if messages %}
        {% for message in messages %}
        <div class="alert alert-info" role="alert">{{ message }}</div>
        {% endfor %}
      {% endif %}
      {% endwith %}
      {% block content %}{% endblock %}
    </div>
    <script ... bootstrap JavaScript ...></script>
  </body>
</html>

Благодаря обновленному базовому шаблону внешний вид приложения уже заметно улучшен без необходимости изменять строки кода Python. Если вы хотите убедиться в этом сами, загрузите копию base.html из репозитория GitHub по ссылкам, приведенным в начале этой главы.

Рендеринг загрузочных форм

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

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

  <div class="mb-3">
    <label for="exampleInputPassword1" class="form-label">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1">
  </div>

Но это слишком просто для нужд Microblog, который включает проверку полей и, возможно, потребуется показывать пользователю ошибки проверки. На странице документации есть раздел о проверке на стороне сервера, в котором показано, как оформить поля с сообщением об ошибке. Вот пример.:

  <div class="col-md-3">
    <label for="validationServer05" class="form-label">Zip</label>
    <input type="text" class="form-control is-invalid" id="validationServer05" aria-describedby="validationServer05Feedback" required>
    <div id="validationServer05Feedback" class="invalid-feedback">
      Please provide a valid zip.
    </div>
  </div>

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

Например, макрос Jinja для текстового поля, подобного показанному выше, будет иметь вид:

{% macro form_field(field) %}
  <div class="mb-3">
    {{ field.label(class='form-label') }}
    {{ field(class='form-control' + (' is-invalid' if field.errors else '')) }}
    {%- for error in field.errors %}
    <div class="invalid-feedback">{{ error }}</div>
    {%- endfor %}
  </div>
{% endmacro %}

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

Поскольку макрос определен в файле с именем bootstrap_wtf.html, который расположен в каталоге templates, он может быть вызван, когда потребуется отобразить поле. Например:

{% import "bootstrap_wtf.html" as wtf %}
...
{{ wtf.form_field(form.username) }}

Макрос отображения полей можно расширить, чтобы он также поддерживал отображение флажков, раскрывающихся списков выбора, кнопок отправки и других типов полей. Он также может принимать второй аргумент с логическим значением, указывающим, следует ли автоматически переводить поле в фокус страницы, что должно быть сделано для первого поля формы. Для еще большего удобства можно создать другой макрос для рендеринга всей формы, просто перебрав поля формы и вызвав form_field() макрос для каждого из них.

Полный bootstrap_wtf.html файл доступен в репозитории GitHub, ссылка на который приведена в начале этой главы. Он включает в себя более полную версию макроса form_field(), показанного выше, и второй макрос с именем quick_form(), который принимает объект формы и отображает все его поля с помощью первого макроса.

Как это выглядит, когда реализовано в реальной форме? Ниже вы можете увидеть переработанный шаблон register.html в качестве примера:

app/templates/register.html: Шаблон регистрации пользователя.

{% extends "base.html" %}
{% import 'bootstrap_wtf.html' as wtf %}

{% block content %}
    <h1>Register</h1>
    {{ wtf.quick_form(form) }}
{% endblock %}

Разве это не здорово? Оператор import вверху работает аналогично импорту Python на стороне шаблона. Который добавляет макрос wtf.quick_form(), который в одной строке кода отображает полную форму, включая ошибки проверки, и все оформлено в соответствии с фреймворком Bootstrap.

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

Рендеринг записей в блоге

Логика представления, которая отображает отдельные записи в блоге, была абстрагирована в подшаблон под названием _post.html. Все, что мне нужно сделать с этим шаблоном, это внести некоторые незначительные корректировки, чтобы он хорошо выглядел в Bootstrap.

app/templates/_post.html: Переработанный подшаблон публикации.

    <table class="table table-hover">
        <tr>
            <td width="70px">
                <a href="{{ url_for('user', username=post.author.username) }}">
                    <img src="{{ post.author.avatar(70) }}" />
                </a>
            </td>
            <td>
                <a href="{{ url_for('user', username=post.author.username) }}">
                    {{ post.author.username }}
                </a>
                says:
                <br>
                {{ post.body }}
            </td>
        </tr>
    </table>

Рендеринг ссылок для разбивки на страницы

Ссылки на страницы - это еще одна область, в которой Bootstrap предоставляет поддержку. Для этого я просто еще раз обратился к документации Bootstrap и адаптировал один из их примеров. Вот как это выглядит на странице index.html:

app/templates/index.html: Переработаны ссылки на страницы.

    ...
    <nav aria-label="Post navigation">
        <ul class="pagination">
            <li class="page-item{% if not prev_url %} disabled{% endif %}">
                <a class="page-link" href="{{ prev_url }}">
                    <span aria-hidden="true">&larr;</span> Newer posts
                </a>
            </li>
            <li class="page-item{% if not next_url %} disabled{% endif %}">
                <a class="page-link" href="{{ next_url }}">
                    Older posts <span aria-hidden="true">&rarr;</span>
                </a>
            </li>
        </ul>
    </nav>

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

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

До и после

Чтобы внести в ваше приложение эти изменения, пожалуйста, загрузите zip-файл для этой главы и соответствующим образом обновите свои шаблоны.

Ниже вы можете увидеть несколько фотографий до и после, чтобы увидеть трансформацию. Имейте в виду, что это изменение было достигнуто не затрагивая ни одной строки кода приложения!

Следующая глава =>

Комментарии (0)