Базовый шаблон web-приложения на Python и Starlette, часть вторая
programming
Базовый шаблон 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
Этот трюк позволит мне во всех шаблонах приложения, если мне нужно получить доступ к его настройкам, обращаться не к объекту 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 = '© {0}, {1}–{2} гг.'.format(
settings.get('SNAME'),
settings.get('SDATE', cast=int),
datetime.now().year)
else:
footer = f'© {settings.get("SNAME")}, {datetime.now().year} г.'
settings.file_values['FOOTER'] = footer
Пишу этот код в конец файла dirs.py
.
Здесь следует обратить внимание, что в результате работы этой программы в объекте settings
появится еще одно поле — FOOTER
. К этом полю я и буду обращаться в коде базового шаблона, где определён тег <footer>
.
Редактируем "подвал"
Открываю в текстовом редакторе файл базового шаблона.
:tabnew webapp/templates/base.html
В коде этого файла меня интересует одна единственная ссылка, ссылка с идентификатором footer-link
. Вот как она выглядит на текущий момент.
Мне необходимо изменить текст этой ссылки. Вместо строки Website
вписываю в эту ссылку следующую строку:
{{ config.get('FOOTER')|safe }}
Здесь следует обратить внимание, что в этой строке я использовал инструменты интерпретатора шаблонов Jinja2, и в том числе фильтр safe
. Вот как выглядит итоговый код в моём текстовом редакторе.
Редактируем главное меню
На текущий момент главное меню 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
этих ссылок, и каждый пункт меню будет вести на собственную страницу.
Вот как выглядит код в моём текстовом редакторе.
Это все задуманные для решения на текущем этапе задачи. Пришло время протестировать их решения в браузере.
Тестируем в браузере
Сохраняю изменения всех файлов в окне текстового редактора. В соседнем терминале запускаю отладочный сервер приложения, при этом убеждаюсь, что в выхлопе сервера отсутствуют ошибки и исключения. Запускаю браузер, вбиваю в его адресной строке адрес стартовой страницы приложения и жму enter
.
Как видно на снимке экрана выше, главное меню содержит пару дополнительных разделов. А в "подвале" содержится копирайт в полном соответствии с замыслом. В настоящий момент в файле настроек в поле SDATE
хранится значение 2023
, и копирайт выглядит соответственно.
Редактирую файл настроек, вписываю в поле SDATE
значение 2024
, что соответствует текущему календарному году. Останавливаю отладочный сервер и запускаю его вновь. В окне браузера обновляю страницу.
Как видно на снимке экрана выше, копирайт теперь показывает одну дату.
Обращаю внимание на главное меню, пробую получить доступ к его разделам. Вот как выглядит теперь меню раздела Website
, имя этого раздела совпадает с полем SNAME
в файле настроек.
Соседний раздел главного меню тоже кликабелен, подтверждать снимком экрана не буду, проверьте сами. Увы, пока все ссылки в разделах меню ведут на текущую страницу, тестировать больше нечего, но это пока...
Сохраняем изменения в Git
Поскольку файл настроек претерпел изменения, нужно обновить его шаблон.
$ cp .env env_template
Добавляю все накопившиеся в файлах приложения изменения в Git.
$ git add .
Вот как выглядит статус хранилища на текущий момент в моём терминале.
Фиксирую транзакцию.
$ git commit -m"Create the base template"
Код приложения после всех выполненных правок можно найти в моём профиле на github.com. А все поставленные для этой демонстрации цели полностью достигнуты.
Подводим промежуточный итог
В этой демонстрации я показал элементарные операции с настройками web-приложения в рамках шаблона и с использованием соответствующих инструментов Jinja2, задал имя будущему web-сайту и определил год начала его существования. Кроме этого, в главном меню приложения благодаря функциональным инструментам Bootstrap появились два раздела и новые ссылки в них. В общем-то всё готово для вёрстки первой редакции стартовой страницы сайта, чем я и займусь в следующем выпуске этого блога.
В рамках этого цикла статей я планирую показать поэтапно разработку системы авторизации пользователей этого web-приложения, для этого мне потребуется подключить в приложение много других полезных инструментов. Насколько далеко я зайду в реализации этого плана, зависит от активности в блоге — ваши посещения, подписки, лайки, комментарии, донаты служат главным мотивирующим меня фактором. Все статьи цикла доступны в хронологическом порядке по метке webapp. Не оставайтесь в стороне, продолжение следует, будет интересно...