Верстаем статический web-сайт с помощью Python3x, конфигурация
Jazz
Опубликован: | 2024-01-24T07:25:02.893950Z |
Отредактирован: | 2024-01-24T07:25:02.893950Z |
Статус: | публичный |
Итак... Приступаем к конфигурации проекта. В процессе конфигурации web-мастеру необходимо создать структуру каталогов разрабатываемого web-приложения, определить и подключить сторонние библиотеки, которые будут использованы в коде, упорядочить хранение файлов и предусмотреть место для файлов каждого используемого диалекта. Кроме этого, в процессе конфигурации определяется способ запуска приложения для отладки и тестирования на рабочем компьютере web-мастера, а так же возможность переноса приложения на сервер. Этим и займёмся в этой демонстрации.
Выбираем название проекта
Web-сайт - это проект, и этот проект нужно как-то назвать. Поскольку в этой демонстрации я показываю только процесс разработки, а полученное в итоге приложение не будет развёрнуто в сеть, хотя отработка такого развёртывания в виртуальную сеть будет показана, я дам этому приложению простое имя - sitefish. Под этим именем приложение будет храниться в одноимённом каталоге на рабочем компьютере web-мастера, в Git и, впоследствии, на сервере сети.
Имя приложения я привяжу к одноимённой метке - sitefish - по ней можно отфильтровать все посвященные его разработке статьи в хронологическом порядке, очень удобно, рекомендую проследовать по предложенной ссылке.
Определяем место хранения файлов проекта
Работать над sitefish я буду под управлением операционной системы Debian - это основная и единственная операционная система на моём домашнем компьютере, она очень хорошо совместима с web-разработкой и удобна для использования и на десктопе, и на сервере. Проект впоследствии необходимо развернуть на сервер сети, и там тоже будет Debian.
В своей повседневной практике работы с компьютером я храню свои проекты, web и не только, в отдельном каталоге с именем workspace - это вложенный каталог в моём "хомяке" - так обычно называют домашний каталог текущего пользователя в операционных системах с linux на борту.
Для sitefish я создам отдельный одноимённый каталог внутри workspace - этот каталог далее в процессе описания я буду называть корневым каталогом sitefish. В корневом каталоге будет храниться вложенный каталог с именем sitefish и вспомогательные файлы проекта - этот каталог далее в процессе этого описания я буду называть базовым каталогом sitefish. В базовом каталоге я буду хранить файлы web-приложения.
Мне удобно работать в консоли, запускаю любой графический эмулятор терминала и ввожу следующую команду.
$ mkdir -p ~/workspace/sitefish/sitefish
Как видно по содержанию команды, в моём распоряжении появились два одноимённых каталога, один из которых вложен внутрь другого. Вхожу в корневой каталог sitefish.
$ cd ~/workspace/sitefish/
Все последующие, описанные в этой демонстрации команды я буду исполнять в этом терминале, находясь в корневом каталоге sitefish.
Создаём структуру каталогов и файлов приложения
В корневом каталоге sitefish мне будут необходимы следующие файлы:
-
файл настроек web-приложения с именем
.env
; -
файл сценария для запуска отладочного сервера с именем
runserver.py
.
В рабочем терминале выполняю последовательно две команды.
$ touch .env
$ touch runserver.py
В базовом каталоге sitefish мне будут необходимы следующие каталоги и файлы:
-
sitefish/__init__.py
- файл конфигурации ASGI-приложения Starlette; -
sitefish/main
- каталог для хранения файлов главной подпрограммы sitefish; -
sitefish/templates
- каталог для хранения html-шаблонов страниц будущего сайта; -
sitefish/main/views.py
- файл обработчиков HTTP запросов главной подпрограммы; -
sitefish/static
и вложенные в негоcss
,js
иimages
- каталог для хранения статических файлов web-приложения, которыми определяется фронтэнд web-приложения; -
index.html
,about.html
иcontacts.html
- шаблоны html-страниц будущего сайта, все три шаблона будут храниться внутри каталогаtemplates
в соответствующем подкаталогеmain
- шаблоны принадлежат главной подпрограмме; -
index.js
- файл JavaScript сценария, пока не привязанный ни к одной из страниц, на этом этапе нужен только для тестирования URL-адреса из каталогаstatic
.
Все перечисленные файлы и каталоги создаю в терминале, последовательно выполнив следующие команды:
$ touch sitefish/__init_.py
$ mkdir sitefish/main
$ mkdir -p sitefish/templates/main
$ mkdir -p sitefish/static/css
$ mkdir -p sitefish/static/js
$ mkdir -p sitefish/static/images
$ touch sitefish/main/__init__.py
$ touch sitefish/main/views.py
$ touch sitefish/templates/main/index.html
$ touch sitefish/templates/main/about.html
$ touch sitefish/templates/main/contacts.html
$ touch sitefish/static/js/index.js
Вот как выглядит мой терминал после проделанных действий.
И давайте взглянем на визуальное отображение полученного дерева каталогов в файловом менеджере.
В сущности, все эти файлы и каталоги можно было создать с помощью файлового менеджера, мыши и соответствующих контекстных меню, но тогда пришлось бы возить по экрану указатель мыши и показывать на каждом шаге снимок экрана. Командная строка даёт возможность решать многие задачи проще, удобнее и быстрее.
Конфигурация ASGI приложения Starlette
Полученное только что дерево файлов и каталогов мне нужно каким-то образом встроить в ASGI-приложение Starlette, для этого открываю в текстовом редакторе, для этой демонстрации я использовал Vim - с ним удобно, файл __init__.py
из базового каталога sitefish
.
$ vim sitefish/__init__.py
Пишу в этот файл следующий код.
# sitefish/__init__.py
## модуль os нужен для привязки ключевых каталогов
import os
## с помощью jinja2 и typing я настрою шаблонизатор
import jinja2
import typing
## базовые инструменты Starlette для создания и настройки
## ASGI-приложения Starlette
from starlette.applications import Starlette
from starlette.config import Config
from starlette.routing import Mount, Route
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates
## библиотека webassets даст возможность минимизировать
## код фронтэнда и скомпоновать таблицы стилей и сценарии
## избегая повторения кода
from webassets import Environment as AssetsEnvironment
from webassets.ext.jinja2 import assets
## подключаю обработчики запросов web-клиентов
## эти функции я создам на следующем шаге разработки
from .main.views import show_index, show_favicon, show_page
## выполняю привязку к ключевым каталогам приложения
base = os.path.dirname(__file__)
static = os.path.join(base, 'static')
templates = os.path.join(base, 'templates')
## выполняю привязку к файлу настроек .env
settings = Config(os.path.join(os.path.dirname(base), '.env'))
## настраиваю шаблонизатор и подключаю к нему
## инструменты webassets
DI = '''typing.Union[str, os.PathLike[typing.AnyStr],
typing.Sequence[typing.Union[str,
os.PathLike[typing.AnyStr]]]]'''.replace('\n', ' ')
class J2Templates(Jinja2Templates):
def _create_env(
self,
directory: DI, **env_options: typing.Any) -> "jinja2.Environment":
loader = jinja2.FileSystemLoader(directory)
assets_env = AssetsEnvironment(static, '/static')
assets_env.debug = settings.get('ASSETS_DEBUG', bool)
env_options.setdefault("loader", loader)
env_options.setdefault("autoescape", True)
env_options.setdefault("extensions", [assets])
env = jinja2.Environment(**env_options)
env.assets_environment = assets_env
return env
## создаю ASGI приложение и определяю его ключевые
## URL-адреса с помощью классов Route и Mount
app = Starlette(
debug=settings.get('DEBUG', cast=bool),
routes=[
Route('/', show_index, name='index'),
Route('/favicon.ico', show_favicon, name='favicon'),
Route('/{page}', show_page, name='page'),
Mount('/static', app=StaticFiles(directory=static), name='static')])
## подключаю в ASGI приложение файл настроек
app.config = settings
## подключаю шаблонизатор html-файлов
app.jinja = J2Templates(directory=templates)
Здесь следует обратить внимание, что на данный момент я определил в web-приложении sitefish четыре ключевых URL-адреса:
-
/
- стартовая страница сайта; -
/favicon.ico
- иконка для избранного; -
/{page}
- группа адресов html-страниц сайта, которая обрабатывается единым обработчиком, адреса в которой имеют единую форму, может дополняться новыми страницами в зависимости от желания web-мастера и наличия в каталоге шаблонов шаблона с именемpage
, об этом чуть позже обязательно поговорим; -
/static
- группа адресов для файлов из каталогаstatic
- статические файлы сайта, таблицы стилей, сценарии JS, изображения и так далее.
В файле конфигурации ASGI я только что использовал пару настроек, которые необходимо определить в файле настроек. Открываю его в текстовом редакторе.
$ vim .env
И пишу в этот файл следующий код.
DEBUG = True
ASSETS_DEBUG = True
Поскольку в карте адресов будущего сайта я определил адрес /favicon.ico
, мне будет необходим файл изображения с таким именем. О макете сайта мы будем говорить чуть позже в этом цикле статей, пока оговорюсь, что для разрабатываемого сайта я буду использовать макет CodeJ, и оформлены его страницы будут точно так же, как страницы сайта, на котором вы сейчас читаете это описание. Копирую favicon.ico в каталог images
, который расположен внутри каталога static
.
Созданное только что ASGI-приложение требует три обработчика с именами:
-
show_index
; -
show_page
; -
show_favicon
.
Эти три функции представления будут размещаться в файле main/views.py
, открываю его в текстовом редакторе.
$ vim sitefish/main/views.py
Пишу в этот файл следующий код:
# sitefish/main/views.py
## с помощью модуля os я выполню привязку
## к файлу favicon.ico в теле соответствующей
## функции представления
import os
## мне потребуются три типа HTTP ответа,
## два из них я возьму из соответствующего модуля Starlette
from starlette.responses import FileResponse, RedirectResponse
## обработчик запросов к URL-адресу
## стартовой страницы сайта
async def show_index(request):
## здесь я при помощи шаблонизатора
## рендерю заданный html-шаблон,
## формирую HTTP ответ и возвращаю его клиенту
return request.app.jinja.TemplateResponse(
'main/index.html',
{'request': request})
## обработчик URL-адреса /{page}
## в адресе клиент передаёт имя страницы
async def show_page(request):
## получаю переданное клиентом имя
## запрашиваемой страницы
page = request.path_params.get('page')
## если это имя совпадает с index.html
if page == 'index.html':
## в этом случае делаю перенаправление
## на стартовую страницу сайта со статус кодом 301
return RedirectResponse(request.url_for('index'), 301)
## если клиент в URL-адресе передал другое имя
## формирую адрес шаблона запрашиваемой страницы
template = f'main/{page}'
## нахожу этот шаблон в каталоге templates
## по адресу в только что созданной переменной
## и рендерю этот шаблон шаблонизатором
## полученный в итоге HTTP ответ возвращаю клиенту
return request.app.jinja.TemplateResponse(
template,
{'request': request})
## обработчик URL-адреса иконки для избранного
async def show_favicon(request):
## если полученный от клиента запрос
## является GET запросом
if request.method == 'GET':
## привязываю базовый каталог sitefish
base = os.path.dirname(os.path.dirname(__file__))
## в базовом каталоге, во вложенном каталоге
## static, в его вложенном каталоге images
## нахожу файл изображения и отдаю его адрес
## классу FileResponse
## полученный в итоге HTTP ответ возвращаю клиенту
return FileResponse(
os.path.join(base, 'static', 'images', 'favicon.ico'))
На текущем этапе такого простого бакэнда для статического сайта будет достаточно, а обработчик show_page
будет создавать HTTP ответ из любого шаблона, имеющегося в соответствующем каталоге.
Сценарий запуска отладочного сервера
Чтобы протестировать полученные в итоге проектирования страницы, необходимо будет запустить отладочный сервер, именно с его помощью я буду исполнять только что созданное ASGI-приложение. Отладочный сервер я инициирую с помощью библиотеки uvicorn, которую совсем скоро установлю.
Сценарий запуска будет храниться в файле runserver.py
, файл в структуре приложения уже имеется, открываю его в текстовом редакторе.
$ vim runserver.py
Код этого сценария достаточно прост, вот как он выглядит.
# runserver.py
## подключаю uvicorn
import uvicorn
if __name__ == '__main__':
## в стандартной идиоме Питона
## запускаю отладочный сервер,
## в качестве параметра передаю серверу
## объект app из файла конфигурации __init__.py
## кроме этого передаю адрес хоста и порт
## на котором отладочный сервер будет слушать сеть
uvicorn.run(
'sitefish:app', host='127.0.0.1',
reload=True, port=5000, log_level='info')
Теперь у меня есть всё, чтобы запустить web-приложение в отладочном сервере, но нет самого главного - я не установил используемые в предложенном коде библиотеки.
Подключаем сторонние библиотеки
В процессе разработки, отладки и тестирования web-приложения удобно запускать его в так называемом виртуальном окружении. Его нужно создать. Выполняю в рабочем терминале следующую команду.
$ python3 -m venv venv
В результате этого в корневом каталоге sitefish появится вложенный каталоге с именем venv
- как заказывал в команде. Это виртуальное окружение нужно активировать.
$ source venv/bin/activate
В результате выполнения этой команды предложение командной строки в рабочем терминале изменится, и мы увидим в начале предложения префикс (venv)
- этот префикс повторяет имя активного виртуального окружения.
Дабы избежать ошибок при установке сторонних библиотек в это виртуальное окружение, обновляю один из его ключевых компонентов - wheel
.
$ pip install --upgrade wheel
И теперь я могу установить в это виртуальное окружение все использованные в коде sitefish библиотеки. Делаю это вот такой простой командой.
$ pip install starlette jinja2 webassets uvicorn
Эта команда покажет в терминале длинный выхлоп, который обязательно должен заканчиваться строчкой Successfully installed и перечнем всех установленных пакетов. Вот как это выглядит в моём терминале.
Виртуальное окружение готово, но перед запуском sitefish нужно сверстать html-шаблоны функциональных страниц сайта, этим сейчас и займёмся.
Верстаем шаблоны
Мне необходимо запустить отладочный сервер и зайти на хост, который сервер обслуживает web-браузером. Файлы html-шаблонов у нас уже есть, но в них нет кода. И сейчас я должен сверстать начальные версии html-страниц будущего сайта. В рамках этой демонстрации достаточно будет трёх страниц. Открываю в текстовом редакторе шаблон стартовой страницы сайта.
$ vim sitefish/templates/main/index.html
На текущем этапе разработки меня устроит простой и поддающийся валидации html-код, вот как он выглядит у стартовой страницы.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Index Page</title>
</head>
<body>
<div>Сайт в стадии разработки...</div>
<div>Стартовая страница.</div>
<div><a href="/about.html">О компании</a></div>
<div><a href="/contacts.html">Контакты</a></div>
</body>
</html>
Открываю в текстовом редакторе шаблон второй страницы.
$ vim sitefish/templates/main/about.html
Код этой страницы повторяет код предыдущего шаблона, но отличается от него в некоторых мелких деталях.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>About</title>
</head>
<body>
<div>Сайт в стадии разработки...</div>
<div>О компании.</div>
<div><a href="/index.html">На главную</a></div>
<div><a href="/contacts.html">Контакты</a></div>
</body>
</html>
И, наконец, открываю шаблон третьей страницы.
$ vim sitefish/templates/main/contacts.html
Ему даю вот такой код.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Contacts</title>
</head>
<body>
<div>Сайт в стадии разработки...</div>
<div>Страница с контактами.</div>
<div><a href="/index.html">На главную</a></div>
<div><a href="/about.html">О компании</a></div>
</body>
</html>
Все три шаблона на текущий момент не имеют оформления, их я буду редактировать ещё не раз на последующих стадиях разработки. Пока этого кода достаточно, чтобы протестировать приложение в браузере.
У нас есть ещё и файл сценария index.js
, с его помощью я протестирую в браузере файлы каталога /static
, его тоже открываю в текстовом редакторе и даю ему следующий простой код.
// index.js
console.log('Hello, world!');
Чуть позже этот код мы увидим в окне браузера при запросе к соответствующему URL-адресу приложения.
Тестируем приложение в браузере
Код разработан, и нам необходимо его протестировать. Сделать это можно с помощью любого web-браузера. Иду в терминал и запускаю отладочный сервер вот такой командой.
(venv) jazz@desktop:~/workspace/sitefish$ python runserver.py
Здесь я показал кроме самой команды ещё и внешний вид приглашения командной строки терминала. В этом терминале активно созданное выше виртуальное окружение venv
. Убеждаюсь, что программа не выдала в терминал ошибок. Запускаю браузер и стучусь по URL-адресу стартовой страницы web-приложения - localhost:5000
.
На стартовой странице обнаруживаю две ссылки и пробую перейти по каждой из них. Жму левой кнопкой мыши первую, и в результате оказываюсь на второй странице будущего сайта.
Опять вижу две ссылки, обращаю внимание на URL-адрес в адресной строке браузера. Пробую перейти по второй ссылке - "Контакты".
И оказываюсь на третьей странице будущего сайта. В моём распоряжении опять есть две ссылки, и я могу вернуться на любую из предложенных страниц. Обращаю внимание на иконку на активной вкладке браузера - она соответствует файлу favicon.ico
из каталога static
.
Попробуем получить доступ к файлу JS-сценария из каталога static
, в этом каталоге есть файл index.js
, вбиваю в адресную строку соответствующий адрес.
Браузер показал код, который я написал в этом файле ранее. Все имеющиеся в карте URL-адресов ASGI-приложения адреса работают в штатном режиме, как запрограммировано, и отладочный сервер на этих адресах показывает ожидаемый контент. Давайте посмотрим в терминал и на его выхлоп.
Для каждого отправленного браузером запроса отладочный сервер показал отчёт, все обработанные сервером запросы имеют статус код 200
и отметку OK
. Ещё раз обращаю внимание на окно браузера, меня интересует иконка на вкладке - иконка отображается. В выхлопе сервера отсутствуют запросы со статус кодом 404
- все протестированные составляющие web-приложения работают. Цель этого этапа разработки достигнута.
Продолжение следует
В следующих стадиях разработки sitefish я последовательно покажу вёрстку уже существующих страниц будущего сайта, поговорим о макете, оформлении страниц, фронтэнде и базовом шаблоне, с помощью которого я минимизирую повторение html-кода в шаблонах страниц и упорядочу таблицы стилей и JS-сценарии. Полное описание стадий разработки этого web-приложения можно найти по одноимённой ссылке в начале этого абзаца или по одноимённой метке в разделе "Метки" ниже.
Друзья... Вопросы, предложения и пожелания можно писать в комментарии, синяя кнопка ниже активна, я обычно отвечаю на комментарии. Лайки и не лайки приветствуются. А цель этой демонстрации полностью достигнута.