Пишем асинхронное web-приложение на Python3, базовый шаблон

newbie

Опубликован:  2021-01-12T08:32:03.391884Z
Отредактирован:  2021-01-12T08:20:09.192345Z
0
0
0
Вы неавторизованы, рекомендую зарегистрироваться и авторизоваться.

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

1. В предыдущих сериях цикла

В предыдущей статье цикла у приложения auriz появилась возможность использовать шаблонизатор Jinja2, была свёрстана стартовая страница с использованием таблиц стилей и ключевых классов Boostrap, кроме этого на стартовой странице появились сценарии JavaScript. Казалось бы, всё замечательно, но текущая реализация стартовой страницы имеет пару весьма существенных недостатков.

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

IqiLa0Py2J.png

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

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

2. Установка дополнительных пакетов

Замечание: все команды здесь и далее я буду вводить в терминале, находясь в базовом каталоге приложения и с активированным виртуальным окружением auriz.

В одном из предыдущих, посвященных разработке auriz выпусков блога, я создал файл base.js в каталоге статики.

FyVtMPOfa3.png

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

git rm -rf auriz/static/js/base.js

aG3r7ztUHF.png

Для группировки и минимизации кода таблиц стилей и сценариев JavaScript я буду использовать библиотеку webassets, которая требует ещё пары зависимостей: cssmin и rjsmin. Устанавливаю все три пакета в виртуальное окружение auriz.

pip install webassets cssmin rjsmin

vKlstxmXXl.png

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

pip freeze > requirements.txt

ZFv1XD4q9f.png

Теперь webassets необходимо интегрировать в приложение и соответствующим образом настроить Jinja2, об этом далее...

3. Расширяем возможности приложения

Пришло время слегка расширить возможности приложения и вшить в окружение auriz библиотеку webassets. Открываю файл конфигурации приложения в текстовом редакторе.

emacs auriz/__init__.py

В этом файле мне потребуется пара новых импортов.

...
from webassets import Environment as AssetsEnvironment
from webassets.ext.jinja2 import assets
...

Кроме этого, я дополню класс J2Templates несколькими строчками.

class J2Templates(Jinja2Templates):
    def get_env(self, directory: str) -> "jinja2.Environment":
        @jinja2.contextfunction
        def url_for(
                context: dict, name: str, **path_params: typing.Any) -> str:
            request = context["request"]
            return request.url_for(name, **path_params)

        loader = jinja2.FileSystemLoader(directory)
        assets_env = AssetsEnvironment(static, '/static')
        assets_env.debug = settings.get('ASSETS_DEBUG', bool)
        env = jinja2.Environment(
            loader=loader, autoescape=True, extensions=[assets])
        env.assets_environment = assets_env
        env.globals["url_for"] = url_for
        return env

FvPUQyPkpw.png

Для полного комплекта необходимо вписать новую переменную в файл настроек .env.

...
ASSETS_DEBUG=False

QsUBtZRa7a.png

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

cp .env env.template

Чтобы убедиться, что всё сделано правильно, пробую запустить отладочный сервер.

CcNErH2LBO.png

Сервер успешно запустился, это значит, что конфигурация приложения работает правильно, можно приступать к разработке базового шаблона auriz, об этом далее...

4. Разрабатываем базовый шаблон приложения

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

cp auriz/templates/main/index.html auriz/templates/base.html

Открываю файл base.html в текстовом редакторе.

emacs auriz/templates/base.html

Первым делом смотрю на заголовок файла - тег <head>, в нём есть группа тегов <meta> - выделяю их в отдельный блок с именем metas, используя синтаксис Jinja2.

  <head>
    {% 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">
      <meta name="description"
            content="{{ request.app.config.get('SITE_DESCRIPTION') }}">
    {% endblock metas %}
  ...
  </head>
...

Выделяю в отдельный блок тег <title>.

...
    {% block title_tag %}
      <title>
        {{ request.app.config.get(
      'SITE_NAME') }}: {% block title %}{% endblock title %}
      </title>
    {% endblock title_tag %}
...

Группу тегов <link> переписываю и выделяю в отдельный блок с именем styles.

...
    {% block styles %}
      <link rel="icon"
            href="{{ request.app.url_path_for(
              'static', path='images/favicon.ico') }}"
            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" type="text/css"
              href="{{ ASSET_URL }}">
      {% endassets %}
    {% endblock styles %}
  </head>
...

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

Теперь нахожу в файле тег <body> и в нём вложенный тег <div id="main-container">, содержимое этого тега тоже выделяю в отдельный блок с именем page_body.

...
    {% block page_body %}
      <div id="main-container" class="container-fluid">
        <div class="row">
          <div id="content"
               class="col-lg-8 col-md-10 col-sm-10
                      col-lg-offset-2 col-md-offset-1 col-sm-offset-1">
            <div class="row">
              <div id="sub-content"
                   class="col-lg-10 col-md-10 col-lg-offset-1 col-md-offset-1">
                {% block page_content %}{% endblock page_content %}
              </div>
            </div>
          </div>
        </div>
      </div>
    {% endblock page_body %}
...

В самом конце файла нахожу группу тегов <script>, выделяю их в отдельный блок с именем scripts и переписываю, используя синтаксис webassets.

...
    {% block scripts %}
      {% assets filters='rjsmin', output='generic/js/vendor.js',
                'vendor/jquery-3.5.1.js',
                'vendor/bootstrap/js/bootstrap.js',
                'vendor/moment.js',
                'vendor/ru.js' %}
        <script src="{{ ASSET_URL }}"></script>
      {% endassets %}
    {% endblock scripts %}
  </body>
...

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

Cохраняю файл. Чтобы использовать базовый шаблон, нужно переписать шаблон index.html, об этом далее...

5. Наследуем от базового шаблона

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

emacs auriz/templates/main/index.html

Удаляю из этого файла абсолютно весь код, и заменяю его новым.

{% extends "base.html" %}

{% block title %}Сначала{% endblock title %}

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

{% block page_content %}
  <!-- main/index.html -->
  <div class="alert alert-warning">
    <div class="today-field"></div>
    <div class="message-text">
       Сайт в стадии разработки, попробуйте зайти позже.
    </div>
  </div>
{% endblock page_content %}

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

Сохраняю файл, запускаю отладочный сервер, иду в браузер и тестирую стартовую страницу.

TUx8laaiSV.png

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

QLtQxC30Ii.png

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

VXIUwC2Kdp.png

Можно видеть, что код минимизирован. Абсолютно то же самое произошло и с файлами сценариев - их код тоже сгруппирован и минимизирован.

Иногда в процессе разработки клиентского кода приложения (frontend) бывает необходимо, чтобы в браузере отображались оригинальные файлы таблиц стилей и сценариев, их состоянием можно управлять при помощи переменной ASSETS_DEBUG в файле настроек приложения .env, если этой переменной задать значение True и перезапустить отладочный сервер, то браузер будет снова использовать оригинальные файлы из каталога static.

EyQ7HWe4bI.png

Всё, цель текущего этапа разработки auriz достигнута, шаблон стартовой страницы приложения разделён на две части, выделен базовый шаблон приложения, a код таблиц стилей и сценариев JavaScript сгруппирован и минимизирован, теперь фронтенд-разработчик получает возможность писать меньше кода в шаблонах за счёт автоматизации webassets.

6. Сохраняем проект в Git

Настало время сделать очередной коммит, но сначала следует посмотреть на статус репозитория.

git status

LdAANWaHUn.png

Как видно на снимке экрана выше, в каталоге статических файлов появилось два вложенных каталога, которые были сгенерированы автоматически в результате использования элементов webassets в шаблонах приложения, и эти каталоги не нужны на Git-сервере. Открываю в текстовом редакторе файл .gitignore и добавляю в его текст эти два каталога.

KzjEnpyjkZ.png

Теперь можно добавить в репу текущие изменения проекта и сделать очередной коммит. Посмотреть текущее состояние auriz можно по этой ссылке.

7. Продолжение следует

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

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

Комментарии: