Приложение «Журнал обучения» уже полностью функционально, но не оформлено стилистически и работает только на локальной машине. В этой главе мы подберем для проекта простое, но профессиональное оформление, а затем развернем его на сервере, чтобы любой желающий мог создать учетную запись.
Для форматирования будет использоваться библиотека Bootstrap — набор инструментов для оформления веб-приложений, с которыми они будут выглядеть профессионально на любых современных устройствах, от большого монитора с плоским экраном до смартфона. Для этого мы применим приложение django-bootstrap5, а вы заодно потренируетесь в использовании приложений, созданных другими разработчиками Django.
Для развертывания «Журнала обучения» будет использоваться Platform.sh — сайт, позволяющий загрузить ваш проект на один из его серверов, чтобы сделать его доступным для любого пользователя с подключением к Сети. Кроме того, мы начнем пользоваться системой управления версиями Git для отслеживания изменений в проекте.
Завершив работу с «Журналом обучения», вы будете уметь разрабатывать простые веб-приложения, придавать им качественный внешний вид и развертывать их на работающих серверах. Вдобавок по мере накопления опыта вы научитесь пользоваться обучающими ресурсами с материалами более высокого уровня.
До сих пор мы намеренно игнорировали оформление приложения, чтобы сосредоточиться на его функциональности. И это вполне разумный подход к разработке, поскольку приложение приносит пользу, только если работает. Конечно, когда приложение начинает работать, оформление выходит на первый план, чтобы пользователи захотели работать с ним.
В этом разделе мы установим приложение django-bootstrap5 и добавим его в проект. Затем используем его для настройки оформления отдельных страниц проекта, чтобы все они имели единый внешний вид и стиль.
Для интеграции Bootstrap в наш проект будет использоваться приложение django-bootstrap5. Оно скачивает необходимые файлы Bootstrap, размещает их в правильных каталогах проекта и предоставляет доступ к стилевым директивам в шаблонах проекта.
Чтобы установить django-bootstrap5, введите в активной виртуальной среде следующую команду:
(ll_env)learning_log$ pip install django-bootstrap5
--пропуск--
Successfully installed beautifulsoup4-4.11.1 django-bootstrap5-21.3
soupsieve-2.3.2.post1
Затем необходимо ввести следующий код для добавления django-bootstrap5 в список INSTALLED_APPS в файле settings.py:
settings.py
--пропуск--
INSTALLED_APPS = (
# Мои приложения.
'learning_logs',
'accounts',
# Сторонние приложения.
'django_bootstrap5',
# Приложения Django по умолчанию.
'django.contrib.admin',
--пропуск--
Создайте новую секцию для приложений, созданных другими разработчиками, и добавьте в нее запись 'django_bootstrap5'. Проследите за тем, чтобы секция располагалась после секции # Мои приложения, но перед секцией, содержащей приложения Django по умолчанию.
По сути, Bootstrap представляет собой большой набор инструментов форматирования. Кроме того, библиотека содержит ряд шаблонов, с помощью которых можно сформировать общий стиль проекта. Пользоваться этими шаблонами намного проще, чем использовать отдельные инструменты оформления. Чтобы просмотреть шаблоны, предоставляемые Bootstrap, перейдите по ссылке http://getbootstrap.com и найдите раздел Examples (Примеры). Мы воспользуемся шаблоном Navbar static, который предоставляет простую панель навигации и контейнер для содержимого страницы.
На рис. 20.1 показано, как будет выглядеть главная страница после применения шаблона Bootstrap к base.html и незначительного изменения index.html.
Рис. 20.1. Вид главной страницы «Журнала обучения» после использования Bootstrap
Шаблон base.html необходимо изменить так, чтобы в нем был задействован шаблон Bootstrap. Новая версия base.html будет представлена в несколько этапов. Код в этом файле объемный; возможно, вы захотите скопировать его с сайта https://ehmatthes.github.io/pcc_3e. Если вы скопируете файл, то вам все равно следует прочитать данный подраздел, чтобы понять, какие изменения были внесены.
Первое изменение в base.html таково: заголовки HTML определяются в файле, чтобы при открытии страницы «Журнала обучения» в строке заголовка браузера выводилось имя сайта. Кроме того, будут добавлены некоторые требования для использования Bootstrap в шаблонах. Удалите все содержимое base.html и замените его следующим кодом:
base.html
❶ <!doctype html>
❷ <html lang="en">
❸ <head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
❹ <title>Learning Log</title>
❺ {% load django_bootstrap5 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
</head>
Сначала файл объявляется как документ HTML ❶, написанный на английском языке ❷. Файл HTML состоит из двух основных частей: заголовка (head) и тела (body). Заголовок файла HTML начинается с открывающего тега <head> ❸ и не содержит контента: он всего лишь передает браузеру информацию, необходимую для правильного отображения страницы. Далее добавляется элемент <title> для страницы; его содержимое будет выводиться в строке заголовка браузера при каждом открытии «Журнала обучения» ❷.
Перед закрытием раздела head мы загружаем коллекцию шаблонных тегов, доступных в django-bootstrap5 ❺. Так, шаблонный тег {% bootstrap_css %} загружает все CSS-файлы, необходимые для реализации стилей Bootstrap. Следующий тег активизирует все интерактивное поведение, которое может использоваться на странице, — например, раздвижные панели навигации. Закрывающий тег </head> указывается в последней строке.
Все настройки стилей Bootstrap теперь доступны в любом шаблоне, который наследуется от base.html. Если вы хотите использовать пользовательские теги шаблонов django-bootstrap5, то каждый шаблон должен содержать тег {% load django_bootstrap5 %}.
Код, определяющий панель в верхней части страницы, получается довольно длинным, поскольку должен хорошо работать как на узких экранах смартфонов, так и на широких экранах мониторов настольных компьютеров. Мы рассмотрим код панели навигации по частям.
Первая часть панели выглядит так:
base.html
--пропуск--
</head>
<body>
❶ <nav class="navbar navbar-expand-md navbar-light bg-light mb-4 border">
<div class="container-fluid">
❷ <a class="navbar-brand" href="{% url 'learning_logs:index' %}">
Learning Log</a>
❸ <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarCollapse" aria-controls="navbarCollapse"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
❹ <div class="collapse navbar-collapse" id="navbarCollapse">
❺ <ul class="navbar-nav me-auto mb-2 mb-md-0">
❻ <li class="nav-item">
❼ <a class="nav-link" href="{% url 'learning_logs:topics' %}">
Topics</a></li>
</ul> <!-- Конец ссылок в левой части панели навигации -->
</div> <!-- Закрывает сворачивающиеся части панели навигации -->
</div> <!-- Закрывает контейнер панели навигации -->
</nav> <!-- Конец панели навигации -->
❽ {% block content %}{% endblock content %}
</body>
</html>
Первый элемент — открывающий тег <body>. Тело файла HTML содержит контент, который будет виден пользователям на странице. Далее расположен тег <nav>, который обозначает раздел навигационных ссылок в верхней части страницы ❶. Весь контент внутри этого элемента оформляется по правилам Bootstrap, устанавливаемым селекторами navbar, navbar-expand-md и другими, которые вы видите в этой части кода. Селектор (selector) определяет, к каким элементам страницы должно применяться правило стилей. Селекторы navbar-light и bg-light оформляют панель навигации, используя тему со светлым фоном. Сокращение mb в mb-4 происходит от margin-bottom, то есть «нижнее поле»; этот селектор гарантирует, что между панелью и остальным контентом страницы остается свободное место. Селектор border создает тонкую рамку вокруг светлого фона, чтобы немного отделить его от остального содержимого страницы.
Тег <div> в следующей строке открывает изменяемый по размеру контейнер, который будет содержать общую панель навигации. Div — сокращение от слова division (деление); вы создаете веб-страницу, деля ее на разделы и определяя правила стиля и поведения, которые применяются к этим разделам. Все правила стиля и поведения, заданные в открывающем теге <div>, влияют на все содержимое до соответствующего закрывающего тега, </div>.
Далее задается название проекта, Learning Log, которое будет отображаться в качестве первого элемента на панели навигации ❷ и служить ссылкой на главную страницу; она будет выводиться на каждой странице проекта, как это было в неотформатированной версии проекта, созданной в предыдущих двух главах. Селектор navbar-brand меняет вид этой ссылки так, чтобы она выделялась на фоне остальных; это оформление становится одной из составляющих фирменной символики сайта.
Шаблон Bootstrap определяет кнопку, которая будет видна, если ширины окна браузера не хватает для горизонтального отображения всей панели навигации ❸. Когда пользователь нажимает кнопку, навигационные элементы выводятся в раскрывающемся списке. Атрибут collapse сворачивает панель при уменьшении размеров окна браузера и отображении сайта на мобильных устройствах с малыми экранами.
Далее открывается новая секция панели навигации (<div>) ❹. Это начало той части панели, которая будет сворачиваться на узких экранах и окнах.
В Bootstrap элементы навигации определяются как элементы неупорядоченного списка ❺, при этом правила стилей делают его совсем непохожим на список. Все ссылки и пункты панели добавляются как элементы неупорядоченного списка ❻. В нашем примере единственный элемент в списке — ссылка на страницу тем ❼. Обратите внимание на закрывающий тег </li> в конце ссылки; каждому открывающему тегу необходим соответствующий закрывающий.
Остальные строки, показанные выше, содержат соответствующие закрывающие теги. В HTML комментарий пишется следующим образом:
<!-- Это HTML-комментарий. -->
Закрывающие теги обычно не имеют комментариев, но если вы еще не набрались опыта работы с HTML, то рекомендуется сопровождать комментариями некоторые закрывающие теги. Всего один пропущенный или лишний тег может нарушить верстку всей страницы. Мы добавили блок content ❽, а также закрывающие теги </body> и </html>.
Мы еще не закончили с панелью навигации, но у нас уже получился полноценный HTML-документ. Если в данный момент сеанс runserver активен, то остановите его и перезапустите сервер. Перейдите на главную страницу проекта, чтобы просмотреть панель навигации с некоторыми элементами, показанными на рис. 20.1 (см. выше). Далее добавим остальные элементы на панель.
Нам все еще нужно добавить ссылки, связанные с учетными записями пользователей. Давайте начнем с добавления всех таких ссылок, кроме формы выхода из системы.
Внесите в файл base.html следующие изменения:
base.html
--пропуск--
</ul> <!-- Конец ссылок в левой части панели навигации -->
<!-- Ссылки, связанные с аккаунтом -->
❶ <ul class="navbar-nav ms-auto mb-2 mb-md-0">
❷ {% if user.is_authenticated %}
<li class="nav-item">
❸ <span class="navbar-text me-2">Hello, {{ user.username }}.
</span></li>
❹ {% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:register' %}">
Register</a></li>
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:login' %}">
Log in</a></li>
{% endif %}
</ul> <!-- Конец ссылок, связанных с аккаунтом -->
</div> <!-- Закрывает сворачивающиеся части панели навигации -->
--пропуск--
Новый набор ссылок начинается с помощью еще одного открывающего тега <ul> ❶. На странице можно создать сколько угодно групп ссылок. Имя селектора ml-auto означает margin-left-automatic, то есть «автоматическое левое поле»; этот селектор анализирует другие элементы на панели навигации и определяет величину левого поля, которое сдвигает эту группу ссылок к правому краю экрана.
Блок if уже использовался ранее для вывода сообщений для пользователей в зависимости от того, выполнили они вход или нет ❷. На этот раз блок стал немного длиннее, поскольку некоторые правила стилей находятся внутри условных тегов. Приветствие для авторизованных пользователей расположено в элементе <span> ❸. Элемент span оформляет фрагменты текста или элементы страницы, которые являются частью более длинной строки. Если элементы div создают собственный раздел страницы, то элементы span следуют непрерывно внутри большего раздела. На первый взгляд такая структура кажется запутанной, поскольку многие страницы содержат элементы div с большой вложенностью. Здесь элемент span используется для оформления текста на панели навигации — например, имени пользователя, выполнившего вход.
В блоке else, который запускается для неавторизованных пользователей, мы добавили ссылки для регистрации новой учетной записи и входа в систему ❹. Они должны выглядеть так же, как и ссылка на страницу тем.
Если вы захотите добавить на панель навигации дополнительные ссылки, то добавьте еще один элемент <li> в одну из групп <ul>, которые мы определили, используя правила стилей, подобные приведенным выше.
Теперь добавим на панель навигации форму выхода из системы.
Изначально мы добавили форму выхода из системы в нижнюю часть файла base.html. Теперь поместим ее в более подходящее место, на панель навигации:
base.html
--пропуск--
</ul> <!-- Конец ссылок, связанных с аккаунтом -->
{% if user.is_authenticated %}
<form action="{% url 'accounts:logout' %}" method='post'>
{% csrf_token %}
❶ <button name='submit' class='btn btn-outline-secondary btn-sm'>
Log out</button>
</form>
{% endif %}
</div> <!-- Закрывает сворачивающиеся части панели навигации -->
--пропуск--
Форма выхода из системы должна располагаться после набора ссылок для управления учетной записью, но внутри сворачивающейся части панели навигации. Единственное изменение в форме — добавление нескольких классов стилей Bootstrap в элемент <button>, которые применяют правила стилей Bootstrap к кнопке выхода из системы ❶.
После перезагрузки главной страницы вы сможете входить и выходить из системы, используя одну из своих учетных записей.
Работа с файлом base.html еще не закончена. Необходимо определить два блока, которые могут использоваться отдельными страницами для размещения относящегося к ним контента.
Оставшаяся часть base.html содержит основной контент страницы:
base.html
--пропуск--
</nav> <!-- Конец панели навигации -->
❶ <main class="container">
❷ <div class="pb-2 mb-2 border-bottom">
{% block page_header %}{% endblock page_header %}
</div>
❸ </div>
{% block content %}{% endblock content %}
</div>
</main>
</body>
</html>
Сначала открывается тег <main> ❶. Элемент main используется для основного контента в теле страницы. В данном случае назначается селектор container, что является простым способом группировки элементов на странице. В этот контейнер будут добавлены два элемента div.
Первый элемент div содержит блок page_header ❷. Мы будем использовать этот блок для отображения заголовка большинства страниц. Чтобы секция выделялась на фоне других частей страницы, мы разместим отступы под заголовком. Отступ (padding) представляет собой пространство между контентом элемента и его границей. Селектор pb-2 — директива Bootstrap, создающая отступы умеренной величины в нижней части оформляемого элемента. Полем (margin) называется пространство между границей элемента и другими элементами страницы. Селектор mb-2 создает небольшое поле в нижней части этого div.
Нам нужна граница в нижней части этого блока, поэтому мы используем селектор border-bottom, который создает тонкую границу в нижней части блока page_header.
Далее определяется еще один элемент div, содержащий блок content ❸. К этому блоку не применяется никакой конкретный стиль, поэтому контент любой страницы можно оформить так, как вы считаете нужным для этой страницы. Файл base.html завершается закрывающими тегами для элементов main, body и html.
Загрузив главную страницу «Журнала обучения» в браузере, вы увидите профессиональную панель навигации, изображенную на рис. 20.1 (см. выше). Попробуйте изменить размеры окна, заметно уменьшив его ширину; панель навигации должна превратиться в кнопку. Щелкните на кнопке, и все ссылки появятся в раскрывающемся списке.
Изменим главную страницу с помощью нового блока header и другого элемента Bootstrap, так называемого джамботрона (jumbotron) — большого блока, который выделяется на общем фоне страницы. Обычно этот элемент используется на главных страницах для размещения краткого описания проекта и призыва к действию, в котором зрителя приглашают принять участие.
Обновленный файл index.html выглядит так:
index.html
{% extends "learning_logs/base.html" %}
❶ {% block page_header %}
❷ <div class="p-3 mb-4 bg-light border rounded-3">
<div class="container-fluid py-4">
❸ <h1 class="display-3">Track your learning.</h1>
❹ <p class="lead">Make your own Learning Log, and keep a list of the
topics you're learning about. Whenever you learn something new
about a topic, make an entry summarizing what you've learned.</p>
❺ <a class="btn btn-primary btn-lg mt-1"
href="{% url 'accounts:register' %}">Register »</a>
</div>
</div>
{% endblock page_header %}
Сначала мы сообщаем Django о том, что следует определение содержимого блока _header страницы ❶. Элемент jumbotron представляет собой пару элементов div, к которому применяется набор стилевых директив ❷. Внешний div настраивает свойства padding и margin, применяет светлый цвет фона и закругленные углы. Внутренний контейнер div изменяется в зависимости от размера окна и также настраивает отступы. Селектор py-4 применяет отступ сверху и снизу к элементу div. Вы можете изменять значения этих свойств и смотреть, как меняется главная страница.
Внутри элемента jumbotron содержатся три элемента. Первый — короткое сообщение Track your learning (Отслеживайте свое обучение), которое дает новым посетителям представление о том, что делает приложение «Журнал обучения» ❸. Элемент h1 — это заголовок первого уровня, а благодаря селектору display-3 заголовок становится более тонким и высоким. Далее добавляется более длинное сообщение с дополнительной информацией о том, что пользователь может сделать со своим дневником ❹. Это сообщение оформлено как абзац lead, который должен выделяться на фоне обычных абзацев текста.
Вместо простой текстовой ссылки мы создаем кнопку, которая предлагает пользователю зарегистрировать свою учетную запись в «Журнале обучения» ❺. Это та же ссылка, что и в заголовке, но кнопка выделяется на фоне страницы и показывает пользователю, что необходимо сделать для того, чтобы приступить к работе над проектом. В результате применения селекторов получается крупная кнопка, обеспечивающая желаемое действие. Код » является сущностью HTML (HTML entity), представляющей две правые угловые скобки (>>). Наконец, закрывающие теги div завершают блок page_header. Поскольку в этом файле всего два элемента div, то нет необходимости обозначать метками закрывающие теги div в листинге. Контент на эту страницу добавляться не будет, поэтому определять на ней блок content не нужно.
Теперь страница выглядит так, как показано на рис. 20.1 (см. выше). Она смотрится намного лучше по сравнению с проектом, в котором оформление не применялось.
Мы усовершенствовали внешний вид страницы входа, но формы входа изменения пока не коснулись. Приведем внешний вид формы в соответствие с остальными элементами страницы, внеся в файл login.html следующие изменения:
login.html
{% extends "learning_logs/base.html" %}
❶ {% load django_bootstrap5 %}
❷ {% block page_header %}
<h2>Log in to your account.</h2>
{% endblock page_header %}
{% block content %}
<form action="{% url 'accounts:login' %}" method='post'>
{% csrf_token %}
❸ {% bootstrap_form form %}
❹ {% bootstrap_button button_type="submit" content="Log in" %}
</form>
{% endblock content %}
Сначала в шаблон загружаются шаблонные теги bootstrap5 ❶. Затем определяется блок page_header, который описывает, для чего нужна страница ❷. Обратите внимание: блок {% if form.errors %} удален из шаблона; django-bootstrap5 управляет ошибками формы автоматически.
Для отображения формы используется шаблонный тег {% bootstrap_form %} ❸; он заменяет тег {{ form.as_div }}, который мы использовали в главе 19, и вставляет правила в стиле Bootstrap в отдельные элементы формы по мере ее отображения. Чтобы создать кнопку отправки, мы используем тег {% bootstrap_button %} с соответствующими аргументами и присваиваем ей метку Log in ❹.
На рис. 20.2 показана форма входа так, как она выглядит сейчас. Страница стала гораздо более чистой, ее оформление последовательно, а предназначение предельно ясно. Попробуйте выполнить вход, указав неверное имя пользователя или пароль; вы увидите, что даже сообщения об ошибках имеют единый стиль оформления и хорошо интегрируются с сайтом в целом.
Рис. 20.2. Страница входа, оформленная с использованием Bootstrap
А теперь позаботимся о том, чтобы страницы для просмотра информации также были оформлены в том же стиле. Начнем со страницы, на которой расположен список тем:
topics.html
{% extends 'learning_logs/base.html' %}
{% block page_header %}
❶ <h1>Topics</h1>
{% endblock page_header %}
{% block content %}
❷ <ul class="list-group border-bottom pb-2 mb-4">
{% for topic in topics %}
❸ <li class="list-group-item border-0">
<a href="{% url 'learning_logs:topic' topic.id %}">
{{ topic.text }}</a>
</li>
{% empty %}
❹ <li class="list-group-item border-0">No topics have been added yet.</li>
{% endfor %}
</ul>
<a href="{% url 'learning_logs:new_topic' %}">Add a new topic</a>
{% endblock content %}
Тег {% load bootstrap5 %} не нужен, поскольку в этом файле не используются никакие шаблонные теги bootstrap5. Заголовок Topics перемещается в блок page_header, и вместо простого абзацного тега ему назначается оформление заголовка <h1> ❶.
Основное содержимое этой страницы — список тем, поэтому мы используем компонент группы списков (list group) Bootstrap для визуализации страницы. Он применяет набор простых правил стилей к списку целиком и к каждому его элементу. Открывая тег <ul>, мы сначала добавляем класс list-group, чтобы применить к списку правила стилей по умолчанию ❷. Мы дополнительно форматируем список, добавляя границу внизу списка, небольшой отступ под списком (pb-2) и поле под нижней границей (mb-4).
К каждому элементу списка добавляется класс list-group-item, и мы меняем стили по умолчанию, удаляя границы вокруг отдельных элементов ❸. Сообщение, которое отображается, когда список пуст, требует тех же классов ❹.
Теперь страница тем отформатирована так же, как и главная.
На странице темы мы используем панели Bootstrap, чтобы выделить каждую запись. Панель (card) — это набор вложенных контейнеров div с гибкими, предопределенными стилями, которые идеально подходят для отображения записей темы:
topic.html
{% extends 'learning_logs/base.html' %}
❶ {% block page_header %}
<h1>{{ topic.text }}</h1>
{% endblock page_header %}
{% block content %}
<p>
<a href="{% url 'learning_logs:new_entry' topic.id %}">Add new entry</a>
</p>
{% for entry in entries %}
❷ <div class="card mb-3">
<!-- Заголовок панели с временной меткой и ссылкой для редактирования -->
❸ <h4 class="card-header">
{{ entry.date_added|date:'M d, Y H:i' }}
❹ <small><a href="{% url 'learning_logs:edit_entry' entry.id %}">
edit entry</a></small>
</h4>
<!-- Тело панели с текстом ввода -->
❺ <div class="card-body">{{ entry.text|linebreaks }}</div>
</div>
{% empty %}
❻ <p>There are no entries for this topic yet.</p>
{% endfor %}
{% endblock content %}
Сначала тема размещается в блоке page_header ❶. Затем удаляется структура неупорядоченного списка, использовавшаяся ранее в этом шаблоне. Вместо того чтобы превращать каждую запись в элемент списка, мы создаем элемент с селектором card ❷. Он имеет два вложенных элемента: первый предназначен для хранения временно́й метки и ссылки для редактирования, а второй — для хранения тела записи. Селектор card учитывает большинство стилей, необходимых для форматирования этого контейнера div; мы настраиваем панель, добавляя небольшое поле к нижней части каждой панели (mb-3).
Первый элемент в панели представляет собой заголовок — элемент <h4> с селектором card-header ❸. Заголовок содержит дату создания записи и ссылку для ее редактирования. Ссылка edit_entry заключается в тег <small>, чтобы она была чуть меньше временно́й метки ❹. Второй элемент представляет собой div с селектором card-body ❺, который размещает текст записи в простом поле на панели. Обратите внимание: код Django для добавления информации на страницу остался прежним; изменились только элементы, влияющие на внешний вид страницы. Поскольку у нас больше нет неупорядоченного списка, мы заменили теги элементов списка вокруг сообщения с пустым списком простыми тегами абзаца ❻.
На рис. 20.3 изображена страница темы с новым оформлением. Функциональность приложения «Журнал обучения» не изменилась, но приложение выглядит более профессионально и привлекательно для пользователя.
Рис. 20.3. Страница темы, оформленная с помощью стилей Bootstrap
Если вы хотите использовать другой шаблон Bootstrap, то действуйте в той же последовательности, которая уже описывалась в этой главе. Скопируйте шаблон в base.html и измените элементы, содержащие контент, чтобы шаблон отображал информацию вашего проекта. Затем воспользуйтесь средствами индивидуального форматирования Bootstrap для оформления содержимого каждой страницы.
ПРИМЕЧАНИЕ
Инструмент Bootstrap имеет отличную документацию. Посетите страницу https://getbootstrap.com и выберите раздел Docs (Документы), чтобы узнать больше о возможностях Bootstrap.
Упражнения
20.1. Другие формы. Мы применили стили Bootstrap к странице login. Внесите аналогичные изменения в другие страницы на базе форм: new_topic, new_entry, edit_entry и register.
20.2. Форматирование проекта Blog. Используйте Bootstrap для форматирования проекта Blog из главы 19.
После того как проекту был придан профессиональный вид, мы развернем его на реальном сервере, чтобы любой пользователь с подключением к Интернету мог работать с приложением. Мы воспользуемся Platform.sh — веб-платформой, позволяющей управлять развертыванием веб-приложений.
Чтобы создать учетную запись, откройте сайт https://platform.sh и нажмите кнопку Free Trial (Бесплатная пробная версия). Учетные записи создаются бесплатно, и Platform.sh предоставляет бесплатный уровень, который на момент написания книги не требует оплаты. Пробный период позволяет развернуть приложение с минимальными затратами ресурсов, давая возможность протестировать проект в реальном развертывании, прежде чем переходить на платный план хостинга.
ПРИМЕЧАНИЕ
На бесплатном уровне Platform.sh существуют свои ограничения, поскольку хостинг-провайдеры борются со спамом и злоупотреблением ресурсами. Текущие лимиты бесплатной пробной версии можно посмотреть на сайте https://platform.sh/free-trial.
Чтобы развернуть проект на серверах Platform.sh и управлять им, вам понадобятся инструменты интерфейса командной строки (Command Line Interface, CLI). Чтобы установить новейшую версию CLI, откройте сайт https://docs.platform.sh/development/cli.html и следуйте инструкциям для своей операционной системы.
В большинстве систем вы можете установить CLI, выполнив следующую команду в терминале:
$ curl -fsS https://platform.sh/cli/installer | php
После выполнения этой команды вам нужно перезагрузить терминал, прежде чем вы сможете использовать CLI.
ПРИМЕЧАНИЕ
Эта команда, вероятно, не будет работать в стандартном терминале Windows. Вы можете использовать инструментарий Windows Subsystem for Linux (WSL) или терминал Git Bash. Если вам нужно установить PHP, то можете использовать программу установки XAMPP с сайта https://apachefriends.org. Если у вас возникли трудности с установкой Platform.sh CLI, то обратитесь к более подробным инструкциям по установке, изложенным в приложении Д.
Вам также нужно установить дополнительный пакет, platformshconfig. Он помогает определить, запущен ли проект в вашей локальной системе или на сервере Platform.sh. В активной виртуальной среде выполните следующую команду:
(ll_env)learning_log$ pip install platformshconfig
Мы будем использовать этот пакет для изменения настроек проекта, когда он будет запущен на реальном сервере.
Удаленному серверу необходимо знать, от каких пакетов зависит наш проект, поэтому мы воспользуемся модулем pip для создания файла со списком. Оставаясь в активной виртуальной среде, введите следующую команду:
(ll_env)learning_log$ pip freeze > requirements.txt
Команда freeze дает модулю pip указание записать имена всех пакетов, в настоящее время установленных в системе, в файл requirements.txt. Откройте файл requirements.txt и просмотрите пакеты и номера версий, установленных в вашей системе:
requirements.txt
asgiref==3.5.2
beautifulsoup4==4.11.1
Django==4.1
django-bootstrap5==21.3
platformshconfig==2.4.0
soupsieve==2.3.2.post1
sqlparse==0.4.2
Приложение «Журнал обучения» уже зависит от семи разных пакетов с конкретными номерами версий, поэтому для его правильной работы требуется конкретная конфигурация среды на удаленном сервере. (Мы установили три из этих пакетов вручную, а четыре были загружены автоматически как зависимости этих пакетов.)
При развертывании «Журнал обучения» Platform.sh устанавливает все пакеты, перечисленные в requirements.txt, и создает среду с теми же пакетами, которые мы используем локально. Поэтому разработчик может быть уверен в том, что развернутый проект будет работать точно так же, как в его локальной системе. Вы поймете, насколько это полезно, когда начнете создавать и вести в своей системе несколько разных проектов.
ПРИМЕЧАНИЕ
Если пакет добавлен в список в вашей системе, но номер версии отличается от показанного, то используйте версию, которая есть в вашей системе.
Для рабочего сервера требуются два дополнительных пакета. Они используются для обслуживания проекта в рабочей среде, где многие пользователи могут выполнять запросы одновременно.
В каталоге с файлом requirements.txt создайте новый файл requirements_remote.txt. Добавьте в него следующие два пакета:
requirements_remote.txt
# Требования для рабочего проекта.
gunicorn
psycopg2
Пакет gunicorn реагирует на запросы по мере их поступления на удаленный сервер; он играет роль рабочего сервера, который мы использовали локально. Пакет psycopg2 необходим для управления со стороны Django базой данных Postgres, которую использует Platform.sh. Postgres — это система баз данных с открытым исходным кодом, которая очень хорошо подходит для рабочих приложений.
Каждый хостинг-провайдер требует определенную конфигурацию для корректной работы проекта на его серверах. В этом подразделе мы добавим три файла конфигурации:
• .platform.app.yaml — основной конфигурационный файл для проекта. Информирует Platform.sh, какой проект мы пытаемся развернуть и какие ресурсы ему требуются, а также содержит команды для сборки проекта на сервере;
• .platform/routes.yaml — определяет маршруты к нашему проекту. Когда Platform.sh получает запрос, именно эта конфигурация помогает направить запрос в наш конкретный проект;
• .platform/services.yaml — определяет все дополнительные сервисы, необходимые нашему проекту.
Все эти файлы имеют формат YAML (YAML Ain’t Markup Language (YAML не является языком разметки)). YAML — это язык, предназначенный для написания конфигурационных файлов; он спроектирован так, чтобы его могли легко читать как люди, так и машины. Вы можете написать или изменить файл YAML вручную, а компьютер сможет его прочитать и однозначно интерпретировать.
Файлы YAML отлично подходят для настройки процесса развертывания, поскольку позволяют управлять этим процессом.
Большинство операционных систем скрывают файлы и папки, начинающиеся с точки, например .platform. Когда вы открываете менеджер файлов, то по умолчанию не видите такие файлы и папки. Но как программисту вам необходимо их видеть. Просмотреть скрытые файлы и папки в разных операционных системах можно, выполнив следующие действия.
• В операционной системе Windows запустите Проводник, а затем откройте любую папку, например Рабочий стол. Перейдите на вкладку View (Вид) и убедитесь, что установлены флажки File name extensions (Расширения имен файлов) и Hidden items (Скрытые элементы).
• В macOS вы можете нажать сочетание клавиш +Shift+. (точка) в любом окне программы Finder.
• В системах Linux, таких как Ubuntu, можно нажать Ctrl+H в любом файловом браузере. Чтобы сделать эту настройку постоянной, откройте менеджер файлов, например Nautilus, и перейдите на вкладку с настройками (обозначена тремя линиями). Установите флажок Show Hidden Files (Показывать скрытые файлы).
Первый файл конфигурации — самый длинный, поскольку управляет всем процессом развертывания. Ниже он представлен частями; вы можете либо ввести его код вручную в текстовом редакторе, либо загрузить копию с сайта по адресу https://ehmatthes.github.io/pcc_3e.
Вот первая часть файла .platform.app.yaml, который должен быть сохранен в том же каталоге, что и файл manage.py:
.platform.app.yaml
❶ name: "ll_project"
type: "python:3.10"
❷ relationships:
database: "db:postgresql"
# Конфигурация приложения при его публикации в Интернете.
❸ web:
upstream:
socket_family: unix
commands:
❹ start: "gunicorn -w 4 -b unix:$SOCKET ll_project.wsgi:application"
❺ locations:
"/":
passthru: true
"/static":
root: "static"
expires: 1h
allow: true
# Размер постоянного диска приложения (в мегабайтах).
❻ disk: 512
Сохраняя этот файл, убедитесь, что в начале его имени указана точка. Если вы опустите точку, то Platform.sh не обнаружит файл и ваш проект не будет развернут.
Сейчас вам не нужно понимать все содержимое файла .platform.app.yaml; я обозначу лишь наиболее важные части конфигурации. Файл начинается с указания имени проекта, 'll_project', соответствующего имени, которое мы использовали при создании проекта ❶. Нужно обозначить и версию Python, которую мы используем (на момент написания книги — 3.10). Список поддерживаемых версий опубликован на сайте https://docs.platform.sh/languages/python.html.
Далее следует раздел relationships, в котором определяются сервисы, необходимые для работы проекта ❷. Здесь указана единственная связь — с базой данных Postgres. Ниже расположен раздел web ❸. Раздел commands:start сообщает Platform.sh, какой процесс следует использовать для обслуживания входящих запросов. Мы указали, что запросы будет обрабатывать инструмент gunicorn ❹. Эта команда заменяет команду python manage.py runserver, которую мы использовали локально.
Раздел locations сообщает Platform.sh, куда отправлять входящие запросы ❺. Большинство запросов должно передаваться через gunicorn; файлы urls.py будут указывать gunicorn, как именно обрабатывать эти запросы. Запросы на статические файлы будут обрабатываться отдельно и обновляться раз в час. Последняя строка показывает, что мы запрашиваем 512 Мбайт дискового пространства на одном из серверов Platform.sh ❻.
Вторая часть файла .platform.app.yaml выглядит следующим образом:
--пропуск--
disk: 512
# Установка локального монтирования для чтения/записи журналов.
❶ mounts:
"logs":
source: local
source_path: logs
# Хуки, выполняемые в различные моменты жизненного цикла приложения.
❷ hooks:
build: |
❸ pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements_remote.txt
mkdir logs
❹ python manage.py collectstatic
rm -rf logs
❺ deploy: |
python manage.py migrate
В разделе mounts ❶ определяются каталоги, в которых будут производиться чтение и запись данных во время работы проекта. В этом разделе задается каталог logs/ для развернутого проекта.
В разделе hooks ❷ определяются действия, которые выполняются в различные моменты процесса развертывания. В разделе build мы указываем команды установки всех пакетов, необходимых для работы проекта в реальной среде ❸. Мы также приводим команду collectstatic ❹, которая собирает все статические файлы, необходимые для проекта, в одной локации, чтобы их можно было эффективно обслуживать.
Наконец, в разделе deploy ❺ мы указываем, что миграции должны выполняться каждый раз при развертывании проекта. В простом проекте это не будет иметь никакого эффекта, если в нем не было изменений.
Два других конфигурационных файла намного короче; рассмотрим их далее.
Этот файл управляет маршрутами. Маршрут (route) — это путь, по которому проходит запрос при его обработке сервером. Получая запрос, Platform.sh должен знать, куда его отправить.
Создайте папку .platform в том же каталоге, где расположен файл manage.py. Убедитесь, что добавили точку в начало имени. В новой папке создайте файл routes.yaml и введите в него следующий код:
.platform/routes.yaml
# Каждый маршрут описывает, как входящий URL будет обработан Platform.sh.
"https://{default}/":
type: upstream
upstream: "ll_project:http"
"https://www.{default}/":
type: redirect
to: "https://{default}/"
Этот файл гарантирует, что такие запросы, как https://project_url.com и www.project_url.com, будут направляться в одно и то же место.
В этом конфигурационном файле перечисляются сервисы, которые необходимы проекту для работы. Сохраните этот файл в каталоге .platform/ вместе с файлом routes.yaml:
.platform/routes.yaml
# Каждый из перечисленных сервисов будет развернут в собственном контейнере
# проекта Platform.sh.
db:
type: postgresql:12
disk: 1024
Этот файл содержит лишь один сервис — базу данных Postgres.
В конец файла settings.py необходимо добавить раздел для определения настроек, предназначенных конкретно для среды Platform.sh:
settings.py
--пропуск--
# Настройки Platform.sh.
❶ from platformshconfig import Config
config = Config()
❷ if config.is_valid_platform():
❸ ALLOWED_HOSTS.append('.platformsh.site')
❹ if config.appDir:
STATIC_ROOT = Path(config.appDir) / 'static'
❺ if config.projectEntropy:
SECRET_KEY = config.projectEntropy
if not config.in_build():
❻ db_settings = config.credentials('database')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': db_settings['path'],
'USER': db_settings['username'],
'PASSWORD': db_settings['password'],
'HOST': db_settings['host'],
'PORT': db_settings['port'],
},
}
Обычно мы помещаем операторы импорта в начало модуля, но в данном случае полезно хранить все настройки удаленного доступа в одном разделе. В этом примере мы импортируем Config из platformshconfig ❶, который помогает определить настройки на удаленном сервере. Мы изменяем настройки только в том случае, если метод config.is_valid_platform() возвращает True ❷, указывая, что настройки используются на сервере Platform.sh.
Мы изменяем значение ALLOWED_HOSTS, чтобы допустить обслуживание проекта хостами, заканчивающимися на .platformsh.site ❸. Все проекты, развернутые на бесплатном уровне, будут обслуживаться на этом хосте. Если настройки загружаются в каталог развернутого приложения ❹, то мы прописываем переменную STATIC_ROOT, чтобы статические файлы обслуживались корректно. Мы также настраиваем на удаленном сервере более безопасный ключ SECRET_KEY ❺.
Наконец, мы настраиваем рабочую базу данных ❻. Она используется только в том случае, если процесс сборки завершен и проект обслуживается. Весь показанный код необходим для успешного взаимодействия Django с сервером Postgres, настроенным Platform.sh для проекта.
Как упоминалось в главе 17, Git — система управления версиями, которая позволяет создать «мгновенный снимок» состояния кода проекта при реализации каждой новой функции. Это дает возможность легко вернуться к последнему работоспособному состоянию проекта при возникновении каких-либо проблем (например, если в ходе работы над новой функцией была случайно внесена ошибка). Каждый снимок называется фиксацией или коммитом (commit).
Если в вашем проекте используется Git, это означает, что вы можете дорабатывать проект, не беспокоясь, что он выйдет из строя. Развертывая проект на сервере, вы должны убедиться в том, что ваша версия проекта работоспособна. Подробная информация о Git и управлении версиями приведена в приложении Г.
Вполне возможно, пакет Git уже установлен в вашей системе. Чтобы проверить это, откройте новое терминальное окно и введите команду git --version:
(ll_env)learning_log$ git --version
git version 2.30.1 (Apple Git-130)
Если вы увидели сообщение о том, что Git не установлен, то обратитесь к инструкциям по установке Git, изложенным в приложении Г.
Git следит за тем, кто внес изменения в проект, даже в том случае, если над проектом работает только один человек. Для этого Git необходимо знать имя пользователя и адрес электронной почты. Имя следует указывать обязательно, но ничего не мешает использовать вымышленный адрес электронной почты для учебных проектов:
(ll_env)learning_log$ git config --global user.name "eric"
(ll_env)learning_log$ git config --global user.email "[email protected]"
Если пропустить этот шаг, то Git запросит у вас эту информацию при первой фиксации.
Нам не нужно, чтобы система Git отслеживала все файлы в проекте, поэтому мы настроим ее на пропуск некоторых файлов. Создайте файл .gitignore в папке с файлом manage.py. Обратите внимание: имя файла начинается с точки, а файл не имеет расширения. Содержимое файла .gitignore выглядит так:
.gitignore
ll_env/
__pycache__/
*.sqlite3
Мы даем Git указание игнорировать весь каталог ll_env, поскольку можем автоматически воссоздать его в любой момент. Кроме того, в системе управления версиями не отслеживается каталог __pycache__ с файлами .pyc, которые создаются автоматически, когда Django выполняет файлы .py. Мы не отслеживаем изменения в локальной базе данных, поскольку так поступать вообще нежелательно: если на сервере будет использоваться SQLite, то вы можете случайно переписать «живую» базу данных локальной тестовой базой данных при отправке проекта на сервер. Благодаря звездочке в выражении *.sqlite3 Git получает указание игнорировать любые файлы с расширением .sqlite3.
ПРИМЕЧАНИЕ
Если вы используете macOS, то добавьте .DS_Store в файл .gitignore. В нем хранится информация о настройках папок в macOS, и он не имеет никакого отношения к этому проекту.
Чтобы инициализировать репозиторий Git для «Журнала обучения», добавьте все необходимые файлы в репозиторий и зафиксируйте исходное состояние проекта. Вот как это делается:
❶ (ll_env)learning_log$ git init
Initialized empty Git repository in /Users/eric/.../learning_log/.git/
❷ (ll_env)learning_log$ git add .
❸ (ll_env)learning_log$ git commit -am "Ready for deployment to Platform.sh."
[main (root-commit) c7ffaad] Ready for deployment to Platform.sh.
42 files changed, 879 insertions(+)
create mode 100644 .gitignore
create mode 100644 .platform.app.yaml
--пропуск--
create mode 100644 requirements_remote.txt
❹ (ll_env)learning_log$ git status
On branch main
nothing to commit, working tree clean
(ll_env)learning_log$
Сначала вводится команда git init, которая инициализирует пустой репозиторий в каталоге, содержащем «Журнал обучения» ❶. Затем команда git add . добавляет все файлы (кроме игнорируемых) в репозиторий ❷. (Не забудьте точку.) Далее вводится команда git commit -am "сообщение": благодаря флагу -a Git получает указание добавить все измененные файлы в фиксированное состояние, а флаг -m дает Git указание сохранить сообщение в журнале ❸.
Вывод команды git status ❹ сообщает, что текущей является главная ветвь, а рабочий каталог пуст. Этот статус должен выводиться каждый раз, когда вы отправляете свой проект на Platform.sh.
На данный момент «Журнал обучения» по-прежнему работает в нашей локальной системе, а также настроен для корректной работы на удаленном сервере. Мы воспользуемся инструментом Platform.sh CLI, чтобы создать новый проект на сервере, а затем загрузить наш проект на удаленный сервер.
В терминале перейдите в каталог learning_log/ и выполните следующую команду:
(ll_env)learning_log$ platform login
Opened URL: http://127.0.0.1:5000
Please use the browser to log in.
--пропуск--
❶ Do you want to create an SSH configuration file automatically? [Y/n] Y
Эта команда откроет вкладку браузера, на которой вы сможете войти в систему. Войдя, вы можете закрыть вкладку браузера и вернуться в терминал. Если будет предложено создать файл конфигурации SSH ❶, то введите Y, чтобы подключиться к удаленному серверу позже.
Теперь создадим проект. Выходных данных будет много, поэтому рассмотрим процесс по частям. Начните с команды create:
(ll_env)learning_log$ platform create
* Project title (--title)
Default: Untitled Project
❶ > ll_project
* Region (--region)
The region where the project will be hosted
--пропуск--
[us-3.platform.sh] Moses Lake, United States (AZURE) [514 gC02eq/kWh]
❷ > us-3.platform.sh
* Plan (--plan)
Default: development
Enter a number to choose:
[0] development
--пропуск--
❸ > 0
* Environments (--environments)
The number of environments
Default: 3
❹ > 3
* Storage (--storage)
The amount of storage per environment, in GiB
Default: 5
❺ > 5
Первое приглашение запрашивает имя для проекта ❶, и мы указываем имя ll_project. Далее появляется запрос, в каком регионе мы хотели бы разместить сервер ❷. Выберите ближайший к вам сервер; для меня это us-3.platform.sh. Для ответа на остальные запросы можно принять значения по умолчанию: сервер на самом низком плане разработки ❸, три окружения для проекта ❹ и 5 Гбайт памяти под весь проект ❺.
Осталось ответить еще на три запроса:
Default branch (--default-branch)
The default Git branch name for the project (the production environment)
Default: main
❶ > main
Git repository detected: /Users/eric/.../learning_log
❷ Set the new project ll_project as the remote for this repository? [Y/n] Y
The estimated monthly cost of this project is: $10 USD
❸ Are you sure you want to continue? [Y/n] Y
The Platform.sh Bot is activating your project
▀▄ ▄▀
█▄█▀███▀█▄█
▀█████████▀
▄▀ ▀▄
The project is now ready!
Репозиторий Git может иметь несколько ветвей; Platform.sh спрашивает, должна ли по умолчанию использоваться ветвь main ❶. Затем появляется запрос, хотим ли мы подключить репозиторий локального проекта к удаленному репозиторию ❷. Наконец, нам сообщают, что этот проект будет стоить около 10 долларов в месяц, если мы будем поддерживать его после окончания бесплатного пробного периода ❸. Если вы еще не вводили реквизиты банковской карты, то вам не стоит беспокоиться об оплате. Platform.sh просто заморозит ваш проект, если вы превысите лимиты бесплатной пробной версии.
Наконец-то все готово для отправки проекта на удаленный сервер. В активном терминальном сеансе введите следующие команды:
(ll_env)learning_log$ platform push
❶ Are you sure you want to push to the main (production) branch? [Y/n] Y
--пропуск--
The authenticity of host 'git.us-3.platform.sh (...)' can't be established.
RSA key fingerprint is SHA256:Tvn...7PM
❷ Are you sure you want to continue connecting (yes/no/[fingerprint])? Y
Pushing HEAD to the existing environment main
--пропуск--
To git.us-3.platform.sh:3pp3mqcexhlvy.git
* [new branch] HEAD -> main
В процессе выполнения команды platform push появится запрос на подтверждение, хотите ли вы загрузить проект ❶. Вдобавок может появиться сообщение о проверке подлинности Platform.sh, если вы впервые подключаетесь к сайту ❷. Введите Y для каждого из этих запросов и увидите, как прокручивается объемный вывод. Поначалу эти результаты, вероятно, покажутся вам непонятными, но если что-то пойдет не так, то они пригодятся при устранении неполадок. Внимательно изучив вывод, вы увидите, как Platform.sh устанавливает необходимые пакеты, собирает статические файлы, выполняет миграции и настраивает URL для проекта.
ПРИМЕЧАНИЕ
Может произойти ошибка из-за простой оплошности, например опечатки в одном из конфигурационных файлов. В этом случае исправьте ошибку в текстовом редакторе, сохраните файл и повторно выполните команду git commit. После этого вы можете снова запустить команду platform push.
После завершения загрузки вы можете открыть проект:
(ll_env)learning_log$ platform url
Enter a number to open a URL
[0] https://main-bvxea6i-wmye2fx7wwqgu.us-3.platformsh.site/
--пропуск--
> 0
Команда platform url перечисляет URL, связанные с развернутым проектом; вам будет предложено выбрать несколько URL, которые подходят для вашего проекта. Выберите один из них, и ваш проект откроется в новой вкладке браузера! Он будет выглядеть точно так же, как и локальная версия, но вы можете поделиться этим URL с любым пользователем в мире, и он сможет получить доступ к вашему проекту и использовать его.
В этом подразделе мы доработаем развернутое приложение и создадим суперпользователя (так же как делали в локальной версии). Заодно повысим уровень защиты проекта, переведя настройку отладочного режима DEBUG в состояние False, чтобы пользователи не получали в сообщениях об ошибке дополнительную информацию, которая может использоваться для проведения атак на сервер.
База данных для рабочего проекта создана, но совершенно пуста. Все пользователи, которых мы создали ранее, существуют только в локальной версии проекта.
Чтобы создать суперпользователя в рабочей версии проекта, мы запустим SSH-сеанс (secure socket shell), в котором сможем выполнять команды управления на удаленном сервере:
(ll_env)learning_log$ platform environment:ssh
___ _ _ __ _
| _ \ |__ _| |_ / _|___ _ _ _ __ __| |_
| _/ / _` | _| _/ _ \ '_| ' \ _(_-< ' \
|_| |_\__,_|\__|_| \___/_| |_|_|_(_)__/_||_|
Welcome to Platform.sh.
❶ web@ll_project.0:~$ ls
accounts learning_logs ll_project logs manage.py requirements.txt
requirements_remote.txt static
❷ web@ll_project.0:~$ python manage.py createsuperuser
❸ Username (leave blank to use 'web'): ll_admin_live
Email address:
Password:
Password (again):
Superuser created successfully.
❹ web@ll_project.0:~$ exit
logout
Connection to ssh.us-3.platform.sh closed.
❺ (ll_env)learning_log$
При первом запуске команды platform environment:ssh вы можете получить сообщение о проверке подлинности этого узла. Если увидите такое сообщение, то введите Y, и откроется сеанс удаленного терминала.
После выполнения команды ssh ваш терминал будет работать так же, как и на удаленном сервере. Обратите внимание, что подсказка изменилась и указывает на сеанс связи с проектом ll_project ❶. Если вы выполните команду ls, то увидите файлы, загруженные на сервер Platform.sh.
Выполните команду createsuperuser, которую мы использовали в главе 18 ❷. На этот раз я ввел имя администратора, ll_admin_live, которое отличается от использовавшегося локально ❸. Завершив работу в удаленной терминальной сессии, выполните команду exit ❹. В подсказке будет указано, что вы снова работаете в локальной системе ❺.
Теперь вы можете добавить каталог /admin/ в конец URL рабочего приложения и перейти на страницу администратора. Если другие пользователи уже используют ваш проект, то имейте в виду, что у вас будет доступ ко всем их данным! Отнеситесь к этому ответственно, и пользователи будут продолжать доверять вам свои данные.
ПРИМЕЧАНИЕ
Пользователи Windows могут использовать те же команды, которые показаны здесь (например, ls вместо dir), поскольку вы работаете с терминалом Linux через удаленное соединение.
В том, как сейчас развернут наш проект, есть одна вопиющая уязвимость: настройка DEBUG = True в файле settings.py, которая выдает отладочные сообщения при возникновении ошибок. Страницы ошибок Django предоставляют важную отладочную информацию при разработке проекта; однако они дают слишком много информации злоумышленникам, если такие страницы будут доступны на рабочем сервере.
Чтобы понять, насколько это опасно, перейдите на главную страницу вашего развернутого проекта. Войдите в учетную запись пользователя и добавьте путь /topics/999/ в конец URL главной страницы. Если вы еще не успели создать тысячи тем, то по этому адресу увидите страницу с сообщением DoesNotExist at /topics/999/. Прокрутив страницу вниз, вы увидите большой объем информации о проекте и сервере. Вряд ли вы хотите, чтобы ваши пользователи видели ее, и уж точно против того, чтобы эта информация была доступна потенциальным злоумышленникам.
Мы можем предотвратить показ этой информации на реальном сайте, установив настройку DEBUG = False в части файла settings.py, относящейся только к развернутой версии проекта. Таким образом, вы продолжите видеть отладочную информацию локально, где она полезна, однако на развернутом сайте она отображаться не будет.
Откройте файл settings.py в вашем редакторе кода и добавьте следующую строку кода в часть, определяющую настройки для Platform.sh:
settings.py
--пропуск--
if config.is_valid_platform():
ALLOWED_HOSTS.append('.platformsh.site')
DEBUG = False
--пропуск--
Вот и все, что нужно сделать в рамках настройки конфигурации для развернутой версии проекта. Когда нужно настроить рабочую версию проекта, мы просто изменяем соответствующую часть конфигурации, созданную ранее.
Теперь изменения, внесенные в settings.py, необходимо зафиксировать, а затем отправить их на Platform.sh. Следующий терминальный сеанс показывает, как это делается:
❶ (ll_env)learning_log$ git commit -am "Set DEBUG False on live site."
[main d2ad0f7] Set DEBUG False on live site.
1 file changed, 1 insertion(+)
❷ (ll_env)learning_log$ git status
On branch main
nothing to commit, working tree clean
(ll_env)learning_log$
Мы вводим команду git commit с коротким, но содержательным сообщением ❶. Напомню, что флаг -am обеспечивает фиксацию всех изменившихся файлов и регистрацию сообщения в журнале. Git видит, что изменился один файл, и фиксирует изменение в репозитории.
Из результата выполнения команды git status понятно, что мы работаем с главной ветвью репозитория, а новые изменения для фиксации отсутствуют ❷. Очень важно проверять информацию о статусе перед отправкой на Platform.sh. Если вы не видите сообщения, значит, некоторые изменения не были зафиксированы и они не будут отправлены на сервер. Попробуйте снова ввести команду commit, а если не уверены в том, как решить проблему, — прочитайте приложение Г, чтобы лучше понять, как работать с Git.
Теперь отправим обновленный репозиторий на Platform.sh:
(ll_env)learning_log$ platform push
Are you sure you want to push to the main (production) branch? [Y/n] Y
Pushing HEAD to the existing environment main
--пропуск--
To git.us-3.platform.sh:wmye2fx7wwqgu.git
fce0206..d2ad0f7 HEAD -> main
(ll_env)learning_log$
Platform.sh видит, что репозиторий обновился, и заново создает проект, чтобы все изменения были учтены. Он не перестраивает базу данных, поэтому мы не потеряли никакие данные.
Чтобы убедиться в том, что эти изменения вступили в силу, снова откройте URL /topics/999/. Вы должны увидеть лишь сообщение Server Error (500), без какой-либо конфиденциальной информации о проекте.
В главе 19 мы настроили приложение «Журнал обучения» так, чтобы при запросе темы или записи, которая не принадлежит пользователю, он получал ошибку 404. Вероятно, вы также сталкивались с примерами ошибок 500 (внутренние ошибки). Ошибка 404 обычно означает, что код Django правильный, но запрашиваемый объект не существует; ошибка 500 обычно означает, что в написанном вами коде есть ошибка (например, ошибка в функции из файла views.py). В настоящее время Django возвращает одну обобщенную страницу ошибки в обеих ситуациях, но мы можем написать собственные шаблоны страниц ошибок 404 и 500, которые соответствуют общему оформлению «Журнала обучения». Эти шаблоны должны находиться в корневом каталоге шаблонов.
В папке learning_log добавьте новую папку templates. Затем создайте новый файл 404.html; полное имя файла имеет вид learning_log/templates/404.html. Код файла выглядит так:
404.html
{% extends "learning_logs/base.html" %}
{% block page_header %}
<h2>The item you requested is not available. (404)</h2>
{% endblock page_header %}
Этот простой шаблон предоставляет ту же информацию, что и обобщенная страница ошибки 404, но его оформление соответствует остальным страницам сайта.
Создайте другой файл 500.html, используя данный код:
500.html
{% extends "learning_logs/base.html" %}
{% block page_header %}
<h2>There has been an internal error. (500)</h2>
{% endblock page_header %}
Новые файлы потребуют внести небольшие изменения в файл settings.py.
settings.py
--пропуск--
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
--пропуск--
},
]
--пропуск--
Благодаря этому изменению Django получает указание искать в корневом каталоге template шаблоны страниц ошибок и любые другие шаблоны, не связанные с конкретным приложением.
Теперь необходимо зафиксировать изменения в шаблоне и отправить их на Platform.sh:
❶ (ll_env)learning_log$ git add .
❷ (ll_env)learning_log$ git commit -am "Added custom 404 and 500 error pages."
3 files changed, 11 insertions(+), 1 deletion(-)
create mode 100644 templates/404.html
create mode 100644 templates/500.html
❸ (ll_env)learning_log$ platform push
--пропуск--
remote: Verifying deploy--пропуск--. done.
To git.us-3.platform.sh:wmye2fx7wwqgu.git
d2ad0f7..9f042ef HEAD -> main
(ll_env)learning_log$
Сначала выдается команда git add . ❶, поскольку в проекте были созданы новые файлы, и теперь нужно дать Git указание начать отслеживание этих файлов. Затем мы фиксируем изменения ❷ и отправляем обновленный проект на Platform.sh ❸.
Теперь страницы ошибок оформлены так же, как остальные страницы сайта, а приложение при возникновении ошибок выглядит более профессионально.
Возможно, вы захотите продолжить разработку «Журнала обучения» после исходной отправки данных на сервер или создать и развернуть собственные проекты. Процесс обновления проектов определен достаточно четко.
Сначала все необходимые изменения вносятся в локальный проект. Если они приводят к появлению новых файлов, то добавьте эти файлы в репозиторий Git с помощью команды git add . (не забудьте поставить точку в конце команды). Эта команда необходима для любого изменения, требующего миграции базы данных, поскольку для каждой миграции генерируется новый файл.
Затем закрепите изменения в репозитории с помощью команды git commit -am "сообщение". Отправьте изменения на Platform.sh, использовав команду platform push. После этого посетите свой проект и убедитесь в том, что предполагаемые изменения вступили в силу.
В данном процессе легко допустить ошибку, поэтому не удивляйтесь, если что-то пойдет не так. Если код не работает, то проанализируйте сделанное и попробуйте найти ошибку. Если это не удается или вы не можете понять, как отменить ошибку, то обращайтесь к рекомендациям, изложенным в приложении В. Не стесняйтесь обращаться за помощью: все программисты учились создавать проекты и задавали те же вопросы, которые возникнут и у вас, так что вы наверняка найдете кого-нибудь, кто согласится помочь. Решение всех возникающих проблем будет помогать развивать ваши навыки до того момента, когда вы начнете создавать содержательные надежные проекты и отвечать на вопросы других людей.
Очень полезно многократно отработать процесс развертывания на одном проекте или серии малых проектов, чтобы получить представление о развертывании. Однако вы должны знать, как удалить проект после развертывания. Кроме того, Platform.sh может ограничивать количество бесплатно развернутых проектов, и загромождать учетную запись учебными проектами нежелательно.
Вы можете удалить проект с помощью CLI следующим образом:
(ll_env)learning_log$ platform project:delete
Появится запрос с подтверждением, хотите ли вы действительно совершить это деструктивное действие. Ответьте утвердительно, и ваш проект будет удален.
Команда platform create также передает локальному Git-репозиторию ссылку на удаленный репозиторий на сервере Platform.sh. Вы можете удалить эту ссылку с помощью командной строки:
(ll_env)learning_log$ git remote
platform
(ll_env)learning_log$ git remote remove platform
Команда git remote выводит список имен всех удаленных URL, связанных с текущим репозиторием. Команда git remote remove имя_удаленного_репозитория удаляет такие URL из локального репозитория.
Вы также можете удалить ресурсы проекта, войдя на сайт Platform.sh и перейдя на панель управления по адресу https://console.platform.sh. На этой странице перечислены все ваши активные проекты. Нажмите кнопку в виде трех точек в строке проекта и выберите пункт Edit Plan (Редактировать план). Откройте страницу с тарифными планами для проекта. Нажмите кнопку Delete Project (Удалить проект) в нижней части страницы, после чего откроется страница подтверждения, на которой вы сможете подтвердить удаление. Даже если вы удалили свой проект с помощью CLI, нелишним будет ознакомиться с панелью управления хостинг-провайдера, на сервере которого вы разместили свой проект.
ПРИМЕЧАНИЕ
При удалении проекта на Platform.sh с локальной версией проекта ничего не происходит. Если никто не использовал ваш развернутый проект и вы просто отрабатываете процесс развертывания, то ничто не мешает вам удалить проект с Platform.sh и развернуть его заново. Только имейте в виду, что если проект при этом перестал работать, то вы, возможно, столкнулись с ограничениями бесплатного тарифного плана хостинг-провайдера.
Упражнения
20.3. Блог в Интернете. Разверните проект Blog, над которым вы работали ранее, на сервере Platform.sh. Проследите за тем, чтобы переменная DEBUG имела значение False: это помешает пользователям видеть полную страницу ошибки Django при возникновении каких-либо проблем.
20.4. Расширенное приложение «Журнал обучения». Добавьте простую функцию в «Журнал обучения» (например, вывод расширенной информации о проекте на главной странице) и отправьте изменение в развернутую копию. Затем попробуйте внести более сложное изменение — например, дайте пользователю возможность сделать тему общедоступной. Для этого в модель Topic добавляется атрибут public (по умолчанию он должен быть равен False), а на страницу new_topic — элемент формы, позволяющий превратить личную тему в общедоступную. После этого проведите миграцию проекта и переработайте файл views.py, чтобы любая общедоступная тема была видимой и для пользователей, не прошедших аутентификацию.
В этой главе вы узнали, как придать вашему проекту простой, но профессиональный внешний вид с помощью библиотеки Bootstrap и приложения django-bootstrap5. В случае применения Bootstrap выбранные вами стили будут работать одинаково практически на всех устройствах, используемых для работы с вашим проектом.
Вы узнали о шаблонах Bootstrap и использовали шаблон Navbar static для создания простого оформления «Журнала обучения». Вы научились использовать элемент jumbotron для визуального выделения сообщений главной страницы и узнали, как оформлять все страницы на сайте в едином стиле.
В последней части проекта вы узнали, как развернуть его на удаленном сервере, чтобы с ним мог работать любой желающий. Вы создали учетную запись Platform.sh и установили инструменты, упрощающие процесс развертывания. Вы использовали Git для фиксации рабочего проекта в репозитории и отправили репозиторий на удаленный сервер Platform.sh. Наконец, вы узнали, как защитить приложение, включив режим DEBUG = False на работающем сервере. Вы также создали собственные страницы ошибок, поэтому неизбежные возникающие ошибки будут выглядеть хорошо управляемыми.
Итак, работа над «Журналом обучения» закончена, и вы можете перейти к созданию собственных проектов. Начните с простых приложений и убедитесь в том, что приложение заработало, прежде чем повысить уровень сложности. Пусть ваше обучение будет интересным! Удачи!