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

programming

Опубликован:
2024-12-07T00:06:46.670520Z
Отредактирован:
2024-12-07T00:06:46.670520Z
Статус:
публичный
18
0
0

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

Имя сайта

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

Разрабатываемое в этом цикле статей приложение теоретически может быть развёрнуто на разных web-сайтах, и мне очень хотелось бы задавать имя сайта при помощи файла настроек приложения (файл с именем .env).

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

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

Доступ к настройкам приложения в шаблонах

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

request.app.config.get('DEBUG')

Это слишком длинная запись, и очень желательно сократить её длину. Для этого открываю в текстовом редакторе файл конфигурации ASGI приложения.

$ vim webapp/__init__.py

Нахожу в тексте этого файла класс с именем J2Templates и добавляю в метод _create_env одну единственную строчку.

env.globals["config"] = settings

aRibl6puOo.png

Этот трюк позволит мне во всех шаблонах приложения, если мне нужно получить доступ к его настройкам, обращаться не к объекту request, а к объекту config. И это впоследствии качественно облегчит жизнь разработчику шаблонов.

Настройки приложения

Настройки приложения хранятся в файле .env в корневом каталоге приложения. В соответствии с поставленной задачей мне необходимо добавить в этот файл два дополнительных поля:

  • SNAME — имя сайта;

  • SDATE — дата начала работы сайта.

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

:edit .env

И дописываю в конец файла два новых поля.

SNAME=Website
SDATE=2023

Таким образом я задал имя сайта как Website, а начало работы сайта как 2023 — прошлый календарный год.

Обработка строки для копирайта

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

:tabnew webapp/dirs.py

Для работы с датами в стандартной библиотеке Python имеется специализированный инструмент, импортирую его.

from datetime import datetime

Строку с копирайтом я буду хранить в переменной с именем footer. Программа будет проверять одно единственное условие и, в зависимости от выполнения или не выполнения этого условия, генерировать строку копирайта. Хранить полученную строку я буду вместе со всеми остальными настройками приложения в объекте settings, который в редактируемом коде уже существует. Вот как выглядит код программы.

footer = None
if settings.get('SDATE', cast=int) < datetime.now().year:
    footer = '&copy; {0}, {1}&ndash;{2} гг.'.format(
        settings.get('SNAME'),
        settings.get('SDATE', cast=int),
        datetime.now().year)
else:
    footer = f'&copy; {settings.get("SNAME")}, {datetime.now().year} г.'
settings.file_values['FOOTER'] = footer

Пишу этот код в конец файла dirs.py.

ZCY7hz20aE.png

Здесь следует обратить внимание, что в результате работы этой программы в объекте settings появится еще одно поле — FOOTER. К этом полю я и буду обращаться в коде базового шаблона, где определён тег <footer>.

Редактируем "подвал"

Открываю в текстовом редакторе файл базового шаблона.

:tabnew webapp/templates/base.html

В коде этого файла меня интересует одна единственная ссылка, ссылка с идентификатором footer-link. Вот как она выглядит на текущий момент.

vHQPcEswa8.png

Мне необходимо изменить текст этой ссылки. Вместо строки Website вписываю в эту ссылку следующую строку:

              {{ config.get('FOOTER')|safe }}

Здесь следует обратить внимание, что в этой строке я использовал инструменты интерпретатора шаблонов Jinja2, и в том числе фильтр safe. Вот как выглядит итоговый код в моём текстовом редакторе.

P4c7wk463X.png

Редактируем главное меню

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

Файл базового шаблона уже открыт в моём текстовом редакторе. Нахожу в коде тег div, содержащий классы collapse navbar-collapse. Дописываю внутрь этого тега следующий код.

        <div class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                {{ config.get('SNAME') }} <b class="caret"></b>
              </a>
              <ul class="dropdown-menu">
                <li>
                  <a href="/">Авторы</a>
                </li>
                <li>
                  <a href="/">Статьи</a>
                </li>
              </ul>
            </li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                Действия <b class="caret"></b>
              </a>
              <ul class="dropdown-menu">
                <li>
                  <a id="login" href="/">Войти</a>
                </li>
                <li>
                  <a id="reg" href="/">Создать пароль</a>
                </li>
              </ul>
            </li>
          </ul>
        </div>

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

Вот как выглядит код в моём текстовом редакторе.

a1KVV8nim6.png

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

Тестируем в браузере

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

U07zg2dRYi.png

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

Редактирую файл настроек, вписываю в поле SDATE значение 2024, что соответствует текущему календарному году. Останавливаю отладочный сервер и запускаю его вновь. В окне браузера обновляю страницу.

wtBtbgLUav.png

Как видно на снимке экрана выше, копирайт теперь показывает одну дату.

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

wAkWkcSWTT.png

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

Сохраняем изменения в Git

Поскольку файл настроек претерпел изменения, нужно обновить его шаблон.

$ cp .env env_template

Добавляю все накопившиеся в файлах приложения изменения в Git.

$ git add .

Вот как выглядит статус хранилища на текущий момент в моём терминале.

sGhhZ4JH17.png

Фиксирую транзакцию.

$ git commit -m"Create the base template"

Код приложения после всех выполненных правок можно найти в моём профиле на github.com. А все поставленные для этой демонстрации цели полностью достигнуты.

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

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

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