Базовый шаблон web-приложения на Python и Starlette

programming

Опубликован:
2024-12-04T23:44:18.144710Z
Отредактирован:
2024-12-04T23:44:18.144710Z
Статус:
публичный
17
0
0

Web-приложение на Python и Starlette, разработку которого мы исследуем в этом цикле статей, уже в ближайшей перспективе будет иметь некоторое множество страниц. Каждая страница будет свёрстана по определённому макету, в котором наряду с индивидуальными для этой страницы элементами будут присутствовать общие для всех страниц элементы. Дабы предотвратить повторение html-кода в каждом новом шаблоне, будет резонно использовать наследование, а это значит, что мне потребуется шаблон, который станет базовым для всего множества страниц web-приложения. В очередном выпуске цикла начнём верстать базовый шаблон.

Окружение

Работа над проектом прерывалась, прежде чем приступить к очередному этапу, следует восстановить окружение. Конструирование базового шаблона, именно этим мы будем заниматься в этой демонстрации, предполагает многократные правки кода шаблона с одновременным тестированием в браузере. Это значит, что мне необходимо будет одновременно работать в текстовом редакторе (Vim мой выбор) и держать отладочный сервер в рабочем состоянии. И поскольку текстовый редактор я запускаю в терминале, целесообразно будет разделить окно терминала.

Запускаю терминал, вхожу в корневой каталог приложения, и запускаю вторую копию терминала с помощью консольной команды — $ sakura &. В результате этих действий на рабочем столе появится второе окно терминала с соответствующим текущим рабочим каталогом. В одном из окон активирую виртуальное окружение и запускаю отладочный сервер. Вот как это выглядит.

gVrii42BhC.png

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

Создаём базовый шаблон

Как и любой другой шаблон, базовый шаблон будет обычным html-файлом, в разметке которого используются инструменты интерпретатора шаблонов Jinja2. Храниться этот шаблон будет в корне каталога templates, и разумным будет дать этому шаблону имя base.html. Создаю файл шаблона.

$ vim webapp/templates/base.html

В этот файл пишу самый обычный html-код.

<!DOCTYPE html>
<html lang="ru">
  <head></head>

  <body></body>
</html>

Внутри тега head выделяю блок тегов <meta>, даю этому блоку имя metas и размещаю внутри него три тега.

    {% block metas %}
      <meta charset="utf-8">
      <meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">
    {% endblock metas %}

Для создания блока я использовал соответствующий синтаксис Jinja2, открыл блок, определил содержимое блока, закрыл блок.

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

    {% block title %}
      <title>Website</title>
    {% endblock title %}

Таблицы стилей базового шаблона я размещу в отдельном блоке с именем styles.

    {% block styles %}
      <link rel="icon"
            href="{{ url_for('favicon') }}"
            type="image/vnd.microsoft.icon">
      {% assets filters='cssmin', output='generic/css/vendor.css',
                'vendor/bootstrap/css/bootstrap.css',
                'vendor/bootstrap/css/bootstrap-theme.css' %}
        <link rel="stylesheet" href="{{ ASSET_URL }}">
      {% endassets %}
    {% endblock styles %}

Здесь следует обратить внимание, что в рамках этого блока для определения файлов, хранящих таблицы стилей, я использовал ещё один инструмент Jinja2assets. Таким образом итоговый файл с таблицами стилей (vendor.css) будет автоматически генерироваться из перечисленных файлов каталога vendor.

В приложении я буду использовать ещё один интерпретатор шаблонов (Mustache.js), который будет работать в клиентской части приложения, шаблоны для него я планирую размещать в блоке с именем templates, создаю его в базовом шаблоне.

{% block templates %}{% endblock templates %}

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

Вот как тег <head> базового шаблона выглядит в окне моего текстового редактора на данный момент.

8DlJ1zDf48.png

Определяем элементы базового шаблона

Перемещаюсь к тегу <body>, в этом теге обычно размещается контент страницы. В теге <body> базового шаблона я определю общие для всех страниц элементы.

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

    <nav id="navigation" class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button"
                  class="navbar-toggle"
                  data-toggle="collapse"
                  data-target=".navbar-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand"
             href="{{ url_for('index') }}">
            <img alt="logo"
                 src="{{ url_for('static', path='images/logo.png') }}"
                 width="28"
                 height="28">
          </a>
        </div>
        <div class="collapse navbar-collapse"></div>
      </div>
    </nav>
    <!-- page content -->

Здесь следует обратить внимание, что на данный момент главное меню будет содержать только логотип приложения. URL-адрес картинки с логотипом я задал в этом коде с помощью служебной функции url_for. Соответствующий этому адресу файл картинки я чуть позже скопирую в соответствующий каталог в дереве каталогов приложения. Впоследствии, по мере разработки приложения, я многократно буду править главное меню, для размещения его содержимого я предусмотрел тег <div> с классом navbar-collapse. Вот как выглядит код главного меню в окне моего текстового редактора.

jrcoFcn9aU.png

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

Вторым общим для всех страниц приложения элементом будет так называемый "подвал", именно так в терминологии (сленге) верстальщиков называют тег <footer>.

    <footer id="footer">
      <div class="container-fluid">
        <div class="footer-block"></div>
        <div class="footer-content">
          <div class="footer-left text-left">
            <img alt="right finger"
                 src="{{ url_for('static', path='images/footer-left.png') }}"
                 width="24"
                 height="24">
          </div>
          <div class="footer-center text-center">
            <a id="footer-link"
               href="{{ url_for('index') }}">Website
            </a>
          </div>
          <div class="footer-right text-right">
            <img alt="left finger"
                 src="{{ url_for('static', path='images/footer-right.png') }}"
                 width="24"
                 height="24">
          </div>
          <div class="footer-bottom"></div>
        </div>
      </div>
    </footer>

Этот тег я размещаю под тегом <nav> и под комментарий, который следует за тегом <nav>. Здесь следует обратить внимание, что для подвала я использовал пару изображений (footer-left.png и footer-right.png), их я чуть позже скопирую в соответствующий каталог дерева.

Сценарии JavaScript являются неотъемлемой частью страниц любого современного web-приложения, для их размещения в коде базового шаблона я определю ещё один блок, дам ему соответствующее имя — scripts.

    {% block scripts %}
      {% assets filters='rjsmin', output='generic/js/vendor-pub.js',
                 'vendor/jquery.js',
                 'vendor/luxon.js',
                 'vendor/bootstrap/js/bootstrap.js',
                 'vendor/mustache.js',
                 'js/custom.js' %}
        <script src="{{ ASSET_URL }}"></script>
      {% endassets %}
    {% endblock scripts %}

В этом блоке я опять использовал assets и подключил с его помощью в базовый шаблон frontend-библиотеки JavaScript. В списке использованных файлов кроме сторонних библиотек я вписал ещё и файл custom.js, его я создам на следующем шаге разработки.

Блок с именем scripts я разместил под тег <footer>, это гарантирует, что сценарии будут исполнены только после загрузки всего кода страницы. Вот как этот блок выглядит в окне моего текстового редактора.

ieFhmZ8Uk1.png

Теперь давайте поговорим о файле custom.js. Поскольку предполагается использование двух интерпретаторов шаблонов (Jinja2 и Mustache.js), их нужно развести по служебным мета-символам, дабы избежать конфликтных ситуаций. Именно для этого и предназначен файл custom.js, который я уже вписал в соответствующий assets. Этот файл необходимо создать. Создаю его, находясь в окне Vim и в его командном режиме.

:split webapp/static/js/custom.js

В этот файл пишу следующий код.

const customTags = ['<%', '%>'];
Mustache.tags = customTags;

Картинки

В элементах базового шаблона, в атрибутах src всех тегов <img> я использовал URL-адреса соответствующих статических файлов. Файлы этих картинок необходимо скопировать в каталог images. В данном цикле статей я буду использовать макет этого сайта, разрабатывать новый макет для меня слишком накладно, увы, поэтому просто копирую соответствующие файлы в соответствующий каталог. И вот как он выглядит в итоге в дереве каталогов приложения.

sPflD7ZLLD.png

На текущем этапе разработки вёрстка базового шаблона завершена, впоследствии я буду править этот шаблон много раз. А пока следует задуматься об использовании базового шаблона.

Использование базового шаблона

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

:tabnew webapp/templates/main/index.html

Удаляю из этого шаблона всё что в нём хранилось до этого момента. Пишу в этот файл новый код.

С помощью соответствующего инструмента Jinja2 делаю шаблон index.html наследником шаблона base.html.

{% extends "base.html" %}

В шаблоне index.html переопределяю блок styles.

{% block styles %}
  {{ super() }}
  {% assets filters='cssmin', output='generic/css/main/index.css',
            'css/base.css' %}
    <link rel="stylesheet" href="{{ ASSET_URL }}">
  {% endassets %}
{% endblock styles %}

Здесь я использовал ещё один инструмент Jinja2, функцию super, и добавил к используемым в блоке styles базового шаблона .css файлам ещё один файл — base.css, этот файл уже существует, но подвергнется правке на следующем шаге.

Точно также переопределяю в шаблоне index.html блок scripts.

{% block scripts %}
  {{ super() }}
  {% assets filters='rjsmin', output='generic/js/main/index.js',
            'js/main/index.js' %}
    <script src="{{ ASSET_URL }}"></script>
  {% endassets %}
{% endblock scripts %}

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

:tabnew webapp/static/css/base.css

И пишу в него следующий код.

html {
  position: relative;
  min-height: 100%;
}

body {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 14pt;
  color: dimgray;
  background-color: beige;
  margin-bottom: 100px;
}

#navigation {
  margin: 2px 4px 0;
  border: 1px solid gainsboro;
  background-image: linear-gradient(to top, ivory, ivory);
  box-shadow: 0 0 4px gainsboro;
  font-size: 12pt;
  color: gainsboro;
}

.navbar-brand {
  padding-top: 10px;
}

#footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 100px;
  background-color: silver;
  background-image: linear-gradient(to bottom, gray, dimgray);
  box-shadow: 0 0 4px silver;
}

.footer-block {
  height: 32px;
}

.footer-left {
  width: 5%;
  float: left;
  margin-top: 5px;
}

.footer-center {
  width: 90%;
  float: left;
  padding-top: 2px;
}

#footer-link {
  color: ivory;
  font-style: italic;
  text-decoration: none;
  text-shadow: 0 0 4px burlywood;
}

#footer-link:hover {
  cursor: pointer;
  color: white;
  text-shadow: 0 0 0 ivory;
}

.footer-right {
  width: 5%;
  float: left;
  margin-top: 5px;
}

.footer-bottom {
  clear: both;
}

Сохраняю изменения всех файлов, в командном режиме Vim это можно сделать с помощью команды :wa.

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

X9BkC8wTr8.png

Как видно на снимке экрана выше, на стартовой странице сайта появилось главное меню с логотипом и так называемый "подвал", хотя в шаблоне index.html на данный момент нет ни одного html-тега.

Полезно будет взглянуть на исходный код стартовой страницы в браузере и обратить внимание, как отображаются в нём теги <link> и <script>. Так же полезно будет увидеть, как изменятся эти теги в содержании исходного кода страницы, если в файле .env соответствующим образом изменить значение поля ASSETS_DEBUG, перезапустить отладочный сервер и обновить страницу в браузере.

Вариант, когда ASSETS_DEBUG имеет значение True, в моём браузере выглядит следующим образом.

3HNtTvTEZF.png

Изменение значения в поле ASSETS_DEBUG на противоположное меняет исходный код страницы в браузере.

Pn0bX9RHNc.png

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

Подводим промежуточный итог

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

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