Приложение Learning Log уже вполне работоспособно, но оно не имеет стилевого оформления и работает только на локальной машине. В этой главе мы определим для проекта простое, но профессиональное оформление, а затем развернем его на сервере, чтобы любой желающий мог создать учетную запись.
Для стилевого оформления будет использоваться библиотека Bootstrap — набор инструментов для оформления веб-приложений, с которыми они будут выглядеть профессионально на любых современных устройствах, от большого монитора с плоским экраном до смартфона. Для этого мы воспользуемся приложением django-bootstrap3, а вы заодно потренируетесь в использовании приложений, созданных другими разработчиками Django.
Для развертывания Learning Log будет использоваться Heroku — сайт, позволяющий загрузить ваш проект на один из его серверов, чтобы сделать его доступным для любого пользователя с подключением к Интернету. Также мы начнем пользоваться системой контроля версий Git для отслеживания изменений в проекте.
Когда работа с Learning Log будет завершена, вы будете уметь разрабатывать простые веб-приложения, придавать им качественный внешний вид и развертывать их на работающих серверах. Также по мере накопления опыта вы научитесь пользоваться ресурсами с материалами более высокого уровня.
До сих пор мы намеренно игнорировали оформление приложения, чтобы сосредоточиться на его функциональности. И это вполне разумный подход к разработке, потому что приложение приносит пользу только в том случае, если оно работает. Конечно, когда приложение начинает работать, оформление выходит на первый план, чтобы пользователи захотели работать с ним.
В этом разделе я кратко опишу приложение django-bootstrap3 и покажу, как интегрировать его в проект и подготовить к развертыванию.
Для интеграции Bootstrap в наш проект будет использоваться приложение django-bootstrap3. Это приложение загружает необходимые файлы Bootstrap, размещает их в правильных каталогах проекта и предоставляет доступ к стилевым директивам в шаблонах проекта.
Чтобы установить django-bootstrap3, введите следующую команду в активной виртуальной среде:
(ll_env)learning_log$ pip install django-bootstrap3
...
Successfully installed django-bootstrap3
Затем необходимо добавить следующий код для включения django-boostrap3 в список INSTALLED_APPS в файле settings.py:
settings.py
...
INSTALLED_APPS = (
...
'django.contrib.staticfiles',
. .# Сторонние приложения
. .'bootstrap3',
. .
# Мои приложения
'learning_logs',
'users',
)
...
Создайте новую секцию для приложений, созданных другими разработчиками, и включите в нее запись 'bootstrap3'. Обычно приложения должны включаться в INSTALLED_APPS,но для надежности прочитайте инструкции по установке конкретного приложения.
Приложение django-bootstrap3 должно включать jQuery — библиотеку JavaScript, которая содержит некоторые интерактивные элементы, предоставляемые шаблоном Bootstrap. Добавьте следующий код в конец settings.py:
settings.py
...
# Мои настройки
LOGIN_URL = '/users/login/'
# Настройки django-bootstrap3
BOOTSTRAP3 = {
. .'include_jquery': True,
. .}
Этот фрагмент избавляет вас от необходимости загружать библиотеку jQuery и размещать ее в правильном каталоге вручную.
По сути Bootstrap представляет собой большой набор инструментов стилевого оформления. Также библиотека содержит ряд шаблонов, которые можно применить к проекту для формирования общего стиля. Если вы только начинаете работать с Bootstrap, вам будет намного проще воспользоваться этими шаблонами, чем использовать отдельные инструменты оформления. Чтобы просмотреть шаблоны, предоставляемые Bootstrap, перейдите в раздел Getting Started на сайте http://getbootstrap.com/; прокрутите страницу до заголовка Examples и найдите раздел Navbars in action. Мы воспользуемся шаблоном Static top navbar, который предоставляет простую панель навигации у верхнего края, заголовок страницы и контейнер для ее содержимого.
На рис. 20.1 показано, как будет выглядеть домашняя страница после применения шаблона Bootstrap к base.html и незначительного изменения index.html.
Рис. 20.1. Домашняя страница Learning Log
Теперь вы знаете, к какому результату мы стремимся, и вам будет проще понять дальнейшие пояснения.
Шаблон base.html необходимо изменить так, чтобы в нем был задействован шаблон Bootstrap. Новая версия base.html будет представлена в несколько этапов.
Первое изменение в base.html: заголовки HTML определяются в файле, чтобы при открытии страницы Learning Log в строке заголовка браузера выводилось имя сайта. Также будут добавлены некоторые требования для использования Bootstrap в шаблонах. Удалите все содержимое base.html и замените его следующим кодом:
base.html
(1) {% load bootstrap3 %}
(2)
(3)
(4)
. .
. .
. .
(5) . .
? . .{% bootstrap_css %}
. .{% bootstrap_javascript %}
?
В точке (1) загружается коллекция шаблонных тегов из django-bootstrap3. Затем файл объявляется как документ HTML (2), написанный на английском языке (3). Файл HTML состоит из двух основных частей, заголовка и тела — заголовок файла начинается в точке (4). Заголовок файла HTML не содержит контента: он всего лишь передает браузеру информацию, необходимую для правильного отображения страницы. В точке (5) включается элемент title страницы; его содержимое будет выводиться в строке заголовка браузера при открытии Learning Log. В точке ? используется один из шаблонных тегов django-bootstrap3, который приказывает Django включить все стилевые файлы Bootstrap. Следующий тег активизирует все интерактивное поведение, которое может использоваться на странице, — например, раздвижные навигационные панели. В точке ? располагается закрывающий тег .
В верхней части страницы определяется навигационная панель:
...
. .
(1) . .
. .
. . . .
. . . .
. . . .
(4) . . . .
. . . .
. .
. .
Первый элемент — открывающий тег
. Тело файла HTML содержит контент, который будет виден пользователям на странице. В точке (1) элементОставшаяся часть base.html содержит основной контент страницы:
...
(1) . .
. .
(2) . . . .{% block header %}{% endblock header %}
. .
. .
(3) . . . .{% block content %}{% endblock content %}
. .
. .
В точке (1) открывается тег div с классом container. Тег div определяет часть веб-страницы, которая может использоваться для любых целей и может оформляться с применением обрамления, пустого пространства вокруг элемента (полей), пустого пространства между содержимым и границей области (отступов), цветов фона и других стилевых правил. Этот конкретный элемент div выполняет функции контейнера для размещения двух элементов: нового блока с именем header (2) и блока content, использовавшегося в главе 18 (3). Блок header содержит сведения о том, какая информация размещается на странице и что можно сделать на странице. Ему назначается класс page-header, который применяет к блоку набор стилевых правил. Блок content размещается в отдельном элементе div без явного задания стилевых классов.
Загрузив домашнюю страницу Learning Log в браузере, вы увидите профессиональную навигационную панель, изображенную на рис. 20.1. Попробуйте изменить размеры окна, заметно уменьшив его ширину; навигационная панель должна превратиться в кнопку. Щелкните на кнопке, и все ссылки появятся в раскрывающемся списке.
Примечание
Эта упрощенная версия шаблона Bootstrap должна работать в большинстве современных браузеров. В старых браузерах некоторые стили могут отображаться некорректно. Полный шаблон, доступный по адресу http://getbootstrap.com/getting-started/#examples/, будет работать почти во всех существующих браузерах.
Изменим домашнюю страницу при помощи нового блока header и другого элемента Bootstrap, так называемого табло (jumbotron) — большого блока, который выдается на общем фоне страницы и может содержать любую информацию на ваше усмотрение. Обычно этот элемент используется на домашних страницах для размещения краткого описания проекта в целом. И раз уж мы занялись домашней страницей, заодно обновим текст сообщения. Файл index.html выглядит так:
index.html
{% extends "learning_logs/base.html" %}
(1) {% block header %}
(2)
. .
{% endblock header %}
{% block content %}
(3)
. .Register an account to make
. .your own Learning Log, and list the topics you're learning about.
. .Whenever you learn something new about a topic, make an entry
. .summarizing what you've learned.
{% endblock content %}
В точке (1) мы сообщаем Django о том, что далее следует определение содержимого блока header. В элементе jumbotron (2) размещается краткий подзаголовок Track your learning, который дает посетителям представление о том, что делает Learning Log.
В точке (3) добавляется текст, который поможет сориентироваться неопытным пользователям. Приложение предлагает посетителю создать учетную запись, а также описывает два основных действия — создание новых тем и создание записей по темам. Страница на рис. 20.1 выглядит намного лучше, чем страница нашего проекта с простейшим оформлением.
Мы усовершенствовали внешний вид страницы входа, но формы входа изменения пока не коснулись. Приведем внешний вид формы в соответствие с остальными элементами страницы:
login.html
{% extends "learning_logs/base.html" %}
(1) {% load bootstrap3 %}
(2){% block header %}
{% endblock header %}
{% block content %}
. .
(3)
. .
{% endblock content %}
В точке (1) в шаблон загружаются шаблонные теги bootstrap3. В точке (2) определяется блок header, который описывает, для чего нужна страница. Обратите внимание: блок {% if form.errors %} удален из шаблона; django-bootstrap3 управляет ошибками формы автоматически.
В точке (3) добавляется атрибут class="form", после чего при отображении формы (4) используется шаблонный тег {% bootstrap_form %}; он заменяет тег {{ form.as_p }}, используемый в главе 19. Шаблонный тег {% booststrap_form %} вставляет правила в стиле Bootstrap в отдельные элементы формы при ее построении. В точке (5) открывается шаблонный тег bootstrap3 {% buttons %}, который добавляет стилевое оформление Bootstrap к кнопкам.
На рис. 20.2 показана форма входа так, как она выглядит сейчас. Страница стала намного чище, ее оформление — последовательно, а предназначение — предельно ясно.
Рис. 20.2. Страница входа, оформленная с использованием Bootstrap
Попробуйте выполнить вход с неверным именем пользователя или паролем; вы увидите, что даже сообщения об ошибках следуют тому же стилю оформления и хорошо интегрируются с сайтом в целом.
Приведем остальные страницы к тому же стилю оформления. Следующей будет преобразована страница new_topic:
new_topic.html
{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}
(1) {% block header %}
{% endblock header %}
{% block content %}
. .
(2)
. .
{% endblock content %}
В основном эти изменения аналогичны тем, которые были применены в login.html: мы загружаем bootstrap3 и добавляем блок header с соответствующим сообщением (1) . Затем в тег
А теперь позаботимся о том, чтобы страницы для просмотра информации также были выдержаны в том же стиле. Начнем со страницы со списком тем:
topics.html
{% extends "learning_logs/base.html" %}
(1) {% block header %}
{% endblock header %}
{% block content %}
{% for topic in topics %}
(2) . . . .
. . . . {{ topic }}
. . . .
{% empty %}
{% endfor %}
(3)
{% endblock content %}
Тег {% load bootstrap3 %} не нужен, потому что в этом файле не используются никакие шаблонные теги bootstrap3. Заголовок Topics добавляется в блок header (1) . Каждая тема оформляется как элемент
Страница темы содержит больше контента, чем большинство страниц, поэтому над ней придется потрудиться. Чтобы записи визуально выделялись, мы воспользуемся панелями Bootstrap. Панель представляет собой элемент div с заранее определенным стилем и идеально подходит для отображения записей темы:
topic.html
{% extends 'learning_logs/base.html' %}
(1) {% block header %}
{% endblock header %}
{% block content %}
{% for entry in entries %}
(2) . .
(3) . .
(4) . . . .
. . . . {{ entry.date_added|date:'M d, Y H:i' }}
(5) . . . .
. . . . . . edit entry
. . . .
. . . .
. .
? . .
. . . .{{ entry.text|linebreaks }}
. .
. .
{% empty %}
. .There are no entries for this topic yet.
{% endfor %}
{% endblock content %}
Сначала тема размещается в блоке header (1) . Затем удаляется структура неупорядоченного списка, использовавшаяся ранее в этом шаблоне. Вместо того чтобы превращать каждую запись в элемент списка, мы создаем в точке (2) элемент div для панели, который содержит два других вложенных элемента div: panel-heading (3) и panel-body (4). Элемент div с классом panel-heading содержит дату записи и ссылку для ее редактирования. Оба элемента оформлены как элементы
В точке ? располагается элемент div с классом panel-body, который содержит фактический текст записи. Обратите внимание: код Django для включения информации на страницу вообще не изменился; изменились только элементы, влияющие на внешний вид страницы.
Рис. 20.3. Страница темы с оформлением Bootstrap
На рис. 20.3 изображена страница темы с новым оформлением. Функциональность приложения Learning Log не изменилась, но приложение выглядит более привлекательно и заманчиво для пользователя.
Примечание
Если вы хотите использовать другой шаблон Bootstrap, действуйте в той же последовательности, которая уже использовалась в этой главе. Скопируйте шаблон в base.html и измените элементы, содержащие контент, чтобы шаблон отображал информацию вашего проекта. Затем воспользуйтесь средствами индивидуального стилевого оформления Bootstrap для оформления содержимого каждой страницы.
Упражнения
20-1. Другие формы: мы применили стили Bootstrap к страницам login и add_topic. Внесите аналогичные изменения в другие страницы на базе форм: new_entry, edit_entry и register.
20-2. Стилевое оформление Blog: используйте Bootstrap для стилевого оформления проекта Blog из главы 19.
После того как проекту был придан профессиональный вид, мы развернем его на реальном сервере, чтобы любой пользователь с подключением к Интернету мог работать с приложением. Мы воспользуемся Heroku — веб-платформой, позволяющей управлять развертыванием веб-приложений.
Процесс для системы Windows несколько отличается от процесса в Linux и OS X. Если вы работаете в Windows, обращайте внимание на врезки, указывающие, какие изменения следует внести в вашей системе.
Чтобы создать учетную запись, откройте сайт https://heroku.com/ и щелкните на одной из регистрационных ссылок. Учетные записи создаются бесплатно, и Heroku предоставляет бесплатный уровень для тестирования проектов в реальных условиях.
Примечание
На бесплатном уровне Heroku существуют свои ограничения (например, количество приложений, которые можно развернуть, и частоту посещения приложения пользователями). Впрочем, эти ограничения достаточно либеральны, чтобы вы могли потренироваться в развертывании приложений без каких-либо затрат.
Чтобы развернуть проект на серверах Heroku и управлять им, вам понадобятся инструменты из пакета Heroku Toolbelt. Чтобы установить новейшую версию Heroku Toolbelt, откройте сайт https://toolbelt.heroku.com/ и выполните указания для своей операционной системы. В них содержится либо однострочная терминальная команда, либо программа установки, которую вы можете загрузить и запустить.
Вам также придется установить ряд пакетов, упрощающих работу проектов Django на реальных серверах. В активной виртуальной среде введите следующие команды:
(ll_env)learning_log$ pip install dj-database-url
(ll_env)learning_log$ pip install dj-static
(ll_env)learning_log$ pip install static3
(ll_env)learning_log$ pip install gunicorn
Обязательно вводите команды по одной, чтобы вы знали, если при установке какого-либо пакета возникнет проблема. Пакет dj-database-url помогает Django взаимодействовать с базой данных, используемой Heroku, пакеты dj-static и static3 позволяют Django правильно управлять статическими файлами, а gunicorn — сервер, способный предоставлять доступ к приложениям в реальной среде. (Статические файлы содержат стилевые правила и файлы JavaScript.)
Примечание
Некоторые из необходимых пакетов могут не установиться в Windows, но не огорчайтесь, если при попытке установки вы получите сообщение об ошибке. Важнее добиться того, чтобы пакеты были установлены Heroku для развернутого приложения, а этим мы займемся в следующем разделе.
Heroku необходимо знать, от каких пакетов зависит наш проект, поэтому мы воспользуемся pip для построения файла со списком. Оставаясь в активной виртуальной среде, введите следующую команду:
(ll_env)learning_log$ pip freeze > requirements.txt
Команда freeze приказывает pip записать имена всех пакетов, в настоящее время установленных в системе, в файл requirements.txt. Откройте файл requirements.txt и просмотрите пакеты и номера версий, установленных в вашей системе (возможно, пользователи Windows не увидят какие-то строки):
requirements.txt
Django==1.8.4
dj-database-url==0.3.0
dj-static==0.0.6
django-bootstrap3==6.2.2
gunicorn==19.3.0
static3==0.6.1
Приложение Learning Log уже зависит от шести разных пакетов с конкретными номерами версий, поэтому для его правильной работы требуется конкретная конфигурация среды. При развертывании Learning Log Heroku устанавливает все пакеты, перечисленные в requirements.txt, и создает среду с теми же пакетами, которые мы используем локально. По этой причине разработчик может быть уверен в том, что развернутый проект будет работать точно так же, как в его локальной системе. Вы поймете, насколько это полезно, когда начнете строить и вести в своей системе несколько разных проектов.
Затем необходимо добавить в список пакет psycopg2, который помогает Heroku управлять базой данных. Откройте файл requirements.txt и добавьте строку psycopg2>=2.6.1. Эта строка устанавливает версию 2.6.1 пакета psycopg2 (или более новую версию, если она доступна):
requirements.txt
Django==1.8.4
dj-database-url==0.3.0
dj-static==0.0.6
django-bootstrap3==6.2.2
gunicorn==19.3.0
static3==0.6.1
psycopg2>=2.6.1
Если какие-либо пакеты не установлены в вашей системе, добавьте их. В итоге ваш файл requirements.txt должен включать каждый из приведенных выше пакетов. Если пакет входит в список вашей системы, но номер версии отличается от приведенной, оставьте версию вашей системы.
Примечание
Если вы работаете в системе Windows, убедитесь в том, что ваша версия requirements.txt соответствует приведенному списку, — независимо от того, какие пакеты вам удалось установить в вашей системе.
Если вы не укажете версию Python, то Heroku будет использовать собственную версию Python по умолчанию. Убедитесь в том, что Heroku использует ту же версию Python, которая используется у вас. В активной виртуальной среде введите команду python --version:
(ll_env)learning_log$ python --version
Python 3.5.0
В этом примере я использую Python 3.5.0. Создайте новый файл с именем runtime.txt в одном каталоге с файлом manage.py и введите следующую команду:
runtime.txt
python-3.5.0
Этот файл должен содержать одну строку с версией Python, заданной точно в показанном формате: python в нижнем регистре, затем дефис и номер версии из трех частей.
Примечание
Если вы получите сообщение об ошибке, в котором сказано, что запрашиваемая исполнительная среда Python недоступна, откройте страницу https://devcenter.heroku.com/ и щелкните на ссылке Python, затем найдите ссылку Specifying a Python Runtime. Просмотрите текст статьи, найдите доступные варианты исполнительной среды и выберите тот вариант, который ближе всего к вашей версии Python.
Затем в конец файла settings.py необходимо добавить раздел для определения настроек, предназначенных конкретно для среды Heroku:
settings.py
...
# Настройки для django-bootstrap3
BOOTSTRAP3 = {
'include_jquery': True,
}
# Настройки Heroku
(1) if os.getcwd() == '/app':
(2) . .import dj_database_url
. .DATABASES = {
. . . .'default': dj_database_url.config(default='postgres://localhost')
. .}
. .
. .# Поддержка заголовка 'X-Forwarded-Proto' для request.is_secure().
(3) . .SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
. .
. .# Разрешены все заголовки хостов.
(4) . .ALLOWED_HOSTS = ['*']
. .# Конфигурация статических ресурсов
(5) . .BASE_DIR = os.path.dirname(os.path.abspath(__file__))
. .STATIC_ROOT = 'staticfiles'
. .STATICFILES_DIRS = (
. . . .os.path.join(BASE_DIR, 'static'),
. .)
В точке (1) используется функция getcwd(), которая возвращает текущий рабочий каталог, из которого был запущен файл. В схеме развертывания Heroku таким каталогом всегда является /app. При локальном развертывании каталог обычно совпадает с именем папки проекта (learning_log в нашем случае). Проверка if гарантирует, что настройки в этом блоке будут применяться только при развертывании проекта в Heroku. Такая структура позволяет создать один файл настроек, который подойдет как для локальной среды разработки, так и для развертывания на сервере.
В точке (2) импортируется модуль dj_database_url, упрощающий настройку базы данных в Heroku. Heroku использует PostgreSQL (или Postgres) — более мощную базу данных, чем SQLite; эти параметры настраивают проект для работы с Postgres в среде Heroku. Остальные настройки обеспечивают поддержку запросов HTTPS (3), правильный доступ к проекту с URL Heroku (4) и правильное размещение статических файлов на Heroku (5).
Файл Procfile сообщает Heroku, какие процессы должны запускаться для правильной работы проекта. Это однострочный файл, который должен быть сохранен под именем Procfile (символ P верхнего регистра, без расширения) в одном каталоге с файлом manage.py. Содержимое Procfile выглядит так:
Procfile
web: gunicorn learning_log.wsgi --log-file -
Эта строка приказывает Heroku использовать для приложения сервер gunicorn, а при запуске приложения загрузить настройки из файла learning_log/wsgi.py. Флаг log-file сообщает Heroku, какие события должны регистрироваться в журнале.
Также необходимо внести изменения в файл wsgi.py для Heroku, потому что конфигурация для Heroku несколько отличается от той, которая использовалась ранее:
wsgi.py
...
import os
from django.core.wsgi import get_wsgi_application
from dj_static import Cling
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "learning_log.settings")
application = Cling(get_wsgi_application())
Мы импортируем приложение Cling, упрощающее организацию доступа к статическим файлам, и используем его для запуска приложения. Этот код также будет работать локально, так что помещать его в блок if не нужно.
В среде Heroku Django собирает все статические файлы и размещает их в одном месте для эффективного управления. Для этих статических файлов мы создадим специальный каталог. В каталоге learning_log, с которыми работали ранее, находится другой каталог с именем learning_log. В этом вложенном каталоге создайте новый подкаталог с именем static; таким образом, полный путь имеет вид learning_log/learning_log/static/. Также необходимо создать временный файл-«заполнитель» в этом каталоге, потому что при загрузке на Heroku пустые каталоги не включаются в проект. Создайте в каталоге static/ файл с именем placeholder.txt:
placeholder.txt
Этот файл необходим для включения в проект каталога learning_log/static/.
Django собирает статические файлы и помещает их в каталог learning_log/static/.
В этом тексте нет ничего особенного; он просто напоминает, почему этот файл был включен в проект.
Если вы используете Linux или OS X, вы можете попробовать использовать сервер gunicorn локально, прежде чем развертывать его на сервере Heroku. В активной виртуальной среде выполните команду heroku local, чтобы запустить процессы, определенные в Procfile:
(ll_env)learning_log$ heroku local
Installing Heroku Toolbelt v4... done
...
forego | starting web.1 on port 5000
(1) web.1 | [2015-08-13 22:00:45 -0800] [12875] [INFO] Starting gunicorn 19.3.0
(2)web.1 | [2015-08-13 22:00:45 -0800] [12875] [INFO] Listening at:
. .http://0.0.0.0:5000 (12875)
(3)web.1 | [2015-08-13 22:00:45 -0800] [12878] [INFO] Booting worker with pid: 12878
При первом выполнении heroku local будет установлен набор пакетов из Heroku Toolbelt. Из выходных данных видно, что в приведенном примере сервер gunicorn был запущен с идентификатором процесса 12875 (1) . В точке (2) gunicorn прослушивает запросы на порте 5000. Кроме того, gunicorn запускает рабочий процесс (12878), который будет помогать в обслуживании запросов (3).
Откройте адрес http://localhost:5000/ и убедитесь в том, что все работает; домашняя страница Learning Log должна выглядеть так же, как она выглядит при использовании сервера Django (runserver). Нажмите Ctrl+C, чтобы остановить процессы, запущенные командой heroku local. При локальной разработке продолжайте использовать runserver.
Примечание
В системе Windows gunicorn работать не будет; пропустите этот шаг, если вы используете Windows. Это не повлияет на возможность развертывания проекта в Heroku.
Если вы дочитали главу 17, то вы уже знаете, что Git — программа контроля версий, которая позволяет создать «мгновенный снимок» состояния кода проекта при реализации каждой новой функции. Это позволяет легко вернуться к последнему работоспособному состоянию проекта при возникновении каких-либо проблем (например, если в ходе работы над новой функцией была случайно внесена ошибка).
Если в вашем проекте используется Git, это означает, что вы можете работать над новой функциональностью, не беспокоясь о том, чтобы ничего не нарушить. Развертывая проект на сервере, вы должны убедиться в том, что вы развертываете работоспособную версию проекта. Если вы захотите больше узнать о Git и контроле версий, обращайтесь к приложению Г.
Git входит в инструментарий Heroku Toolbelt, так что пакет Git уже должен быть установлен в вашей системе. Однако система Git будет недоступна в терминальных окнах, которые были открыты перед установкой Heroku Toolbelt, поэтому откройте новое терминальное окно и введите команду git --version:
(ll_env)learning_log$ git --version
git version 2.5.0
Если вы получите сообщение об ошибке, обращайтесь к инструкциям по установке Git в приложении Г.
Git следит за тем, кто внес изменения в проект, даже в том случае, если над проектом работает только один человек. Для этого Git необходимо знать ваше имя пользователя и адрес электронной почты. Имя пользователя ввести обязательно, но ничто не мешает вам ввести вымышленный адрес электронной почты для учебных проектов:
(ll_env)learning_log$ git config --global user.name "ehmatthes"
(ll_env)learning_log$ git config --global user.email "eric@example.com"
Если вы забудете об этом шаге, Git запросит у вас эту информацию при первом закреплении.
Нам не нужно, чтобы система Git отслеживала все файлы в проекте, поэтому мы прикажем Git игнорировать некоторые файлы. Создайте файл с именем .gitignore в папке с файлом manage.py. Обратите внимание: имя файла начинается с точки, а файл не имеет расширения. Содержимое .gitignore выглядит так:
.gitignore
ll_env/
__pycache__/
*.sqlite3
Мы приказываем Git игнорировать весь каталог ll_env, потому что мы можем автоматически воссоздать его в любой момент. Также в системе контроля не отслеживается каталог __pycache__ с файлами .pyc, которые создаются автоматически, когда Django выполняет файлы .py. Мы не отслеживаем изменения в локальной базе данных, потому что так поступать вообще нежелательно: если на сервере будет использоваться SQLite, вы можете случайно переписать «живую» базу данных локальной тестовой базой данных при отправке проекта на сервер.
Примечание
Если вы используете Python 2.7, замените __pycache__ на *.pyc, потому что Python 2.7 не создает каталог __pycache__.
Чтобы инициализировать репозиторий Git для Learning Log, добавьте все необходимые файлы в репозиторий и закрепите исходное состояние проекта. Вот как это делается:
(1) (ll_env)learning_log$ git init
Initialized empty Git repository in /home/ehmatthes/pcc/learning_log/.git/
(2)(ll_env)learning_log$ git add .
(3)(ll_env)learning_log$ git commit -am "Ready for deployment to heroku."
[master (root-commit) dbc1d99] Ready for deployment to heroku.
43 files changed, 746 insertions(+)
create mode 100644 .gitignore
create mode 100644 Procfile
--snip--
create mode 100644 users/views.py
(4)(ll_env)learning_log$ git status
# On branch master
nothing to commit, working directory clean
(ll_env)learning_log$
В точке (1) вводится команда git init, которая инициализирует пустой репозиторий в каталоге, содержащем Learning Log. В точке (2) команда git add. добавляет все файлы (кроме игнорируемых) в репозиторий (не забудьте точку). В точке (3) вводится команда git commit -am сообщение: флаг -a приказывает Git включить все измененные файлы в закрепленное состояние, а флаг -m приказывает Git сохранить сообщение в журнале.
Вывод команды git status (4) сообщает, что текущей является главная ветвь, а рабочий каталог пуст. Этот статус должен выводиться каждый раз, когда вы отправляете свой проект на Heroku.
Наконец-то все готово для отправки проекта на сервер Heroku. В активном терминальном сеансе введите следующие команды:
(1) (ll_env)learning_log$ heroku login
Enter your Heroku credentials.
Email: eric@example.com
Password (typing will be hidden):
Logged in as eric@example.com
(2)(ll_env)learning_log$ heroku create
Creating afternoon-meadow-2775... done, stack is cedar-14
https://afternoon-meadow-2775.herokuapp.com/ |
. .https://git.heroku.com/afternoon-meadow-2775.git
Git remote heroku added
(3)(ll_env)learning_log$ git push heroku master
...
remote: -----> Launching... done, v6
(4)remote: . . . .https://afternoon-meadow-2775.herokuapp.com/ deployed to Heroku
remote: Verifying deploy.... done.
To https://git.heroku.com/afternoon-meadow-2775.git
bdb2a35..62d711d master -> master
(ll_env)learning_log$
Сначала войдите на сервер Heroku в терминальном сеансе с именем пользователя и паролем, использованными при создании учетной записи на https://heroku.com/ (1) . Затем прикажите Heroku построить пустой проект (2). Heroku генерирует имя, состоящее из двух слов и числа; позднее вы сможете его изменить. Затем вводится команда git push heroku master (3), которая приказывает Git отправить главную ветвь проекта в репозиторий, только что созданный Heroku. После этого Heroku строит проект на своих серверах с использованием этих файлов. В точке (4) указывается URL, который будет использоваться для обращения к развернутому проекту.
После ввода этих команд проект развернут, но еще не настроен полностью. Чтобы проверить, что серверный процесс был запущен правильно, введите команду heroku ps:
(ll_env)learning_log$ heroku ps
(1) Free quota left: 17h 40m
(2)=== web (Free): `gunicorn learning_log.wsgi __log-file -`
web.1: up 2015/08/14 07:08:51 (~ 10m ago)
(ll_env)learning_log$
В выходных данных указано, сколько еще времени проект может оставаться активным на протяжении следующих 24 часов (1) . На момент написания книги Heroku позволяет бесплатно развернутым проектам оставаться активными до 18 часов за 24-часовой период. При нарушении этого лимита отображается стандартная серверная страница ошибки; вскоре мы изменим ее. В точке (2) запускается процесс, определенный в Procfile.
Теперь мы можем открыть приложение в браузере командой heroku open:
(ll_env)learning_log$ heroku open
Opening afternoon-meadow-2775... done
Команда избавляет вас от необходимости открывать браузер и вводить URL, который вы получили от Heroku, но при желании сайт можно открыть и так. Вы увидите домашнюю страницу Learning Log с правильным оформлением. Впрочем, с приложением еще работать нельзя, потому что база данных не подготовлена.
Примечание
Процесс развертывания приложений на серверах Heroku время от времени изменяется. Если у вас возникнут неразрешимые проблемы, обращайтесь к документации Heroku за помощью. Откройте страницу https://devcenter.heroku.com/ и щелкните на ссылке Python, затем найдите ссылку Getting Started with Django. Если вы не понимаете то, что там написано, обратитесь к рекомендациям в приложении В.
Вы должны выполнить команду migrate, чтобы подготовить базу данных и применить все миграции, сгенерированные в ходе разработки. Для выполнения команд Django и Python в проектах Heroku используется команда heroku run. Пример выполнения команды migrate в среде разработки Heroku:
(1) (ll_env)learning_log$ heroku run python manage.py migrate
(2)Running `python manage.py migrate` on afternoon-meadow-2775... up, run.2435
...
(3)Running migrations:
...
Applying learning_logs.0001_initial... OK
Applying learning_logs.0002_entry... OK
Applying learning_logs.0003_topic_user... OK
Applying sessions.0001_initial... OK
(ll_env)learning_log$
Сначала мы вводим команду heroku run python manage.py migrate (1) . Heroku создает терминальный сеанс для выполнения команды migrate (2). В точке (3) Django применяет миграции по умолчанию, а также миграции, сгенерированные в ходе разработки Learning Log.
Теперь при обращении к развернутому приложению вы сможете использовать его так же, как это делалось в локальной системе. Однако при этом вы не увидите никакие данные, введенные при локальном развертывании, потому что мы не скопировали данные на сервер. Это обычная практика: локальные данные почти никогда не копируются на сервер, потому что они чаще всего являются тестовыми.
Если вы перешлете ссылку на Heroku, получатель сможет работать с вашей версией приложения Learning Log. В следующем разделе мы выполним еще несколько операций, чтобы завершить процесс развертывания и подготовиться к дальнейшей разработке Learning Log.
В этом разделе мы доработаем развернутое приложение и создадим суперпользователя (так же, как это было сделано в локальной версии). Заодно мы повысим уровень защиты проекта, переведя настройку отладочного режима DEBUG в состояние False, чтобы пользователи не получали в сообщениях об ошибке дополнительной информации, которая может использоваться для проведения атак на сервер.
Вы уже видели, что для выполнения одиночных команд может использоваться команда heroku run. Однако команды также можно выполнять, открыв терминальный сеанс Bash при подключении к серверу Heroku командой heroku run bash. Bash — язык, который работает во многих терминалах Linux. Мы используем терминальный сеанс Bash для создания суперпользователя, чтобы иметь возможность обратиться к административному сайту в развернутом приложении:
(ll_env)learning_log$ heroku run bash
Running `bash` on afternoon-meadow-2775... up, run.6244
(1) ~ $ ls
learning_log learning_logs manage.py Procfile requirements.txt runtime.txt
users
staticfiles
(2)~ $ python manage.py createsuperuser
Username (leave blank to use 'u41907'): ll_admin
Email address:
Password:
Password (again):
Superuser created successfully.
(3)~ $ exit
exit
(ll_env)learning_log$
В точке (1) команда ls выводит информацию о файлах и каталогах, существующих на сервере; это те же файлы, которые присутствуют в нашей локальной системе. В этой файловой системе можно перемещаться так же, как и в любой другой.
Примечание
Пользователи Windows должны использовать те же команды (например, ls вместо dir), потому что они работают с терминалом Linux через удаленное подключение.
В точке (2) выполняется команда создания суперпользователя. Она выдает тот же вывод, который был получен в локальной системе при создании суперпользователя в главе 18. После того как создание суперпользователя в терминальном сеансе будет завершено, введите команду exit для возвращения к терминальному сеансу локальной системы (3).
Теперь вы можете добавить /admin/ в конец URL-адреса развернутого приложения, чтобы войти на административный сайт. У меня этот URL-адрес имеет вид https://afternoon-meadow-2775.herokuapp.com/admin/.
Если другие пользователи уже начали работать с вашим проектом, учтите, что вам будут доступны все их данные! Относитесь к конфиденциальности серьезно, и пользователи начнут доверять вам свои данные.
Вероятно, вы бы предпочли использовать более удобные и запоминающиеся URL-адреса, чем https://afternoon-meadow-2775.herokuapp.com/. Чтобы переименовать приложение, достаточно одной команды:
(ll_env)learning_log$ heroku apps:rename learning-log
Renaming afternoon-meadow-2775 to learning-log... done
https://learning-log.herokuapp.com/ | https://git.heroku.com/learning-log.git
Git remote heroku updated
(ll_env)learning_log$
Имя может содержать буквы, цифры и дефисы и выбирается произвольно (при условии, что никто другой еще не занял это имя). Развернутая версия теперь доступна по адресу https://learning-log.herokuapp.com/. По предыдущему URL-адресу проект теперь недоступен; команда apps:rename полностью перемещает проект на новый URL-адрес.
Примечание
При развертывании проекта на бесплатном сервисе Heroku переводит развернутое приложение в спящий режим, если оно не получало запросов в течение определенного периода времени или было слишком активным для бесплатного уровня. При первом обращении к сайту после перехода в спящий режим загрузка займет больше времени, но последующие запросы будут обрабатываться быстрее. Такой подход позволяет Heroku предоставлять бесплатное развертывание.
В текущем варианте развертывания проекта существует одна очевидная проблема: настройка DEBUG=True в файле settings.py, включающая вывод отладочных сообщений при возникновении ошибок. Страницы ошибок Django предоставляют критическую отладочную информацию при разработке проекта, но они также дают слишком много информации хакерам, если оставить их включенными на рабочем сервере. Также необходимо проследить за тем, чтобы никто не мог получать информацию или перенаправлять запросы, выдавая себя за хост проекта.
Изменим файл settings.py, чтобы сообщения об ошибках выводились локально, но не в развернутой версии:
settings.py
...
# Настройки Heroku
if os.getcwd() == '/app':
...
# Поддержка заголовка 'X-Forwarded-Proto' для request.is_secure().
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
. .
. .# Хостом проекта может быть только Heroku.
(1) . .ALLOWED_HOSTS = ['learning-log.herokuapp.com']
(2) . .DEBUG = False
# Конфигурация статических ресурсов.
...
Необходимо внести только два изменения: в точке (1) изменяется настройка ALLOWED_HOSTS, чтобы хостом проекта мог быть только сервер Heroku. Используйте имя своего приложения — сгенерированное Heroku (как afternoon-meadow-2775.herokuapp.com в нашем примере) или выбранное вами. В точке (2) переменной DEBUG присваивается значение False, чтобы при возникновении ошибок не раскрывалась конфиденциальная информация.
Теперь изменения, внесенные в settings.py, необходимо закрепить в репозитории Git, а затем отправить их на Heroku. Следующий терминальный сеанс показывает, как это делается:
(1) (ll_env)learning_log$ git commit -am "Set DEBUG=False for Heroku."
[master 081f635] Set DEBUG=False for Heroku.
1 file changed, 4 insertions(+), 2 deletions(-)
(2)(ll_env)learning_log$ git status
# On branch master
nothing to commit, working directory clean
(ll_env)learning_log$
Мы вводим команду git commit с коротким, но содержательным сообщением (1) . Напомню, что флаг -am обеспечивает закрепление всех изменившихся файлов и регистрацию сообщения в журнале. Git видит, что изменился один файл, и закрепляет изменение в репозитории.
В точке (2) из статусной информации видно, что мы работаем с главной ветвью репозитория, а новые изменения для закрепления отсутствуют. Очень важно проверять статусную информацию перед отправкой на Heroku. Если вы не видите сообщение, значит, некоторые изменения не были закреплены, и они не будут отправлены на сервер. Попробуйте снова ввести команду commit, а если вы не уверены в том, как решить проблему, — прочитайте приложение Г, чтобы лучше понять, как работать с Git.
Теперь отправим обновленный репозиторий на Heroku:
(ll_env)learning_log$ git push heroku master
...
remote: -----> Python app detected
remote: -----> Installing dependencies with pip
...
remote: -----> Launching... done, v8
remote: . . . .https://learning-log.herokuapp.com/ deployed to Heroku
remote: Verifying deploy.... done.
To https://git.heroku.com/learning-log.git
4c9d111..ef65d2b master -> master
(ll_env)learning_log$
Heroku видит, что репозиторий обновился, и заново строит проект, чтобы все изменения были учтены. База данных при этом не воссоздается, поэтому выполнять migrate для этого обновления не придется.
Чтобы убедиться в том, что развертывание стало более безопасным, введите URL проекта с расширением, которое не было определено. Например, попробуйте открыть страницу http://learning-log.herokuapp.com/letmein/. Должна появиться обобщенная страница, которая не сообщает никакой конкретной информации о проекте. Если же вы попробуете применить тот же запрос с локальной версией Learning Log по адресу http://localhost:8000/letmein/, должна появиться стандартная страница ошибки Django. Получается именно то, что нужно: вы будете получать содержательные сообщения об ошибках в процессе разработки проекта, но оградите пользователей от критической информации о коде проекта.
В главе 19 мы настроили приложение Learning Log так, чтобы при запросе темы или записи, которая ему не принадлежит, пользователь получал ошибку 404. Вероятно, вы также сталкивались с примерами ошибок 500 (внутренние ошибки). Ошибка 404 обычно означает, что код Django правилен, но запрашиваемый объект не существует; ошибка 500 обычно означает, что в написанном вами коде существует ошибка (например, ошибка в функции из views.py). В настоящее время Django возвращает одну обобщенную страницу ошибки в обеих ситуациях, но мы можем написать собственные шаблоны страниц ошибок 404 и 500, которые соответствуют общему оформлению Learning Log. Эти шаблоны должны находиться в корневом каталоге шаблонов.
В папке learning_log/learning_log создайте новую папку с именем templates. Затем создайте новый файл с именем 404.html:
404.html
{% extends "learning_logs/base.html" %}
{% block header %}
. .
{% endblock header %}
Этот простой шаблон предоставляет ту же информацию, что и обобщенная страница ошибки 404, но его оформление соответствует остальным страницам сайта.
Создайте другой файл с именем 500.html:
500.html
{% extends "learning_logs/base.html" %}
{% block header %}
. .
{% endblock header %}
Новые файлы потребуют небольших изменений в settings.py.
settings.py
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
. . . .'DIRS': [os.path.join(BASE_DIR, 'learning_log/templates')],
'APP_DIRS': True,
...
},
]
...
Это изменение приказывает Django искать шаблоны страниц ошибок в корневом каталоге шаблонов.
Если вы хотите посмотреть, как будут выглядеть страницы ошибок, в своей системе перед отправкой на сервер Heroku, вам сначала придется установить в локальных настройках режим Debug=False, чтобы подавить выдачу отладочных страниц Django по умолчанию. Для этого внесите следующие изменения в settings.py (убедитесь в том, что вы работаете в части settings.py, относящейся к локальной среде, — а не той, которая относится к Heroku):
settings.py
...
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['localhost']
...
Если DEBUG присвоено значение False, в ALLOWED_HOSTS должен быть указан хотя бы один хост. Теперь запросите тему или запись, которая вам не принадлежит, чтобы увидеть страницу ошибки 404. Затем запросите несуществующий URL-адрес (например, localhost:8000/letmein/), чтобы увидеть страницу ошибки 500.
Завершив проверку, верните DEBUG значение True, чтобы продолжить разработку Learning Log. (Проследите за тем, чтобы настройка DEBUG содержала False в разделе settings.py, относящемся к развертыванию в среде Heroku.)
Примечание
Страница ошибки 500 не содержит никакой информации о текущем пользователе, потому что Django не включает контекстную информацию в ответ при возникновении ошибки сервера.
Теперь необходимо закрепить изменения в шаблоне и отправить их на Heroku:
(1) (ll_env)learning_log$ git add .
(2)(ll_env)learning_log$ git commit -am "Added custom 404 and 500 error pages."
3 files changed, 15 insertions(+), 10 deletions(-)
create mode 100644 learning_log/templates/404.html
create mode 100644 learning_log/templates/500.html
(3)(ll_env)learning_log$ git push heroku master
...
remote: Verifying deploy.... done.
To https://git.heroku.com/learning-log.git
2b34ca1..a64d8d3 master -> master
(ll_env)learning_log$
В точке (1) выдается команда git add ., потому что в проекте были созданы новые файлы, и теперь нужно приказать Git начать отслеживание этих файлов. Затем мы закрепляем изменения (2) и отправляем обновленный проект на Heroku (3). Теперь страницы ошибок имеют такое же оформление, как остальные страницы сайта, а приложение выглядит более профессионально при возникновении ошибок.
На данный момент, если пользователь вручную запрашивает несуществующую тему или запись, он получает ошибку сервера 500. Django пытается отобразить страницу, но не располагает достаточной информацией для этого, что приводит к ошибке 500. Такая ситуация более точно обрабатывается как ошибка 404, и это поведение можно реализовать при помощи вспомогательной функции Django get_object_or_404(). Эта функция пытается получить запрошенный объект из базы данных, а если этот объект не существует — инициирует исключение 404. Мы импортируем эту функцию в views.py и используем ее вместо get():
views.py
...
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, Http404
...
@login_required
def topic(request, topic_id):
"""Выводит одну тему и все ее записи."""
. .topic = get_object_or_404(Topic, id=topic_id)
# Проверка того, что тема принадлежит текущему пользователю.
...
Теперь при запросе несуществующей темы (например, http://localhost:8000/topics/999999/) появляется страница ошибки 404. Чтобы развернуть это изменение, выполните закрепление и отправьте проект на Heroku.
Возможно, вы захотите продолжить разработку Learning Log после исходной отправки данных на сервер или создать и развернуть собственные проекты. Существует достаточно четко определенный процесс обновления проектов.
Сначала все необходимые изменения вносятся в локальный проект. Если изменения приводят к появлению новых файлов, добавьте эти файлы в репозиторий Git командой git add . (не забудьте точку в конце команды). Эта команда необходима для любого изменения, требующего миграции базы данных, потому что для каждой миграции генерируется новый файл миграции.
Затем закрепите изменения в репозитории командой git commit -am "сообщение". Отправьте изменения на Heroku командой git push heroku master. Если вы провели локальную миграцию базы данных, также необходимо провести миграцию и для базы данных развернутого приложения. Либо используйте одноразовую команду heroku run python manage.py migrate, либо откройте удаленный терминальный сеанс командой heroku run bash и выполните команду python manage.py migrate. Затем посетите свой проект и убедитесь в том, что предполагаемые изменения вступили в силу.
В этом процессе легко допустить ошибку, так что не удивляйтесь, если что-то пойдет не так. Если код не работает, проанализируйте то, что было сделано, и попробуйте найти ошибку. Если найти ошибку не удается или вы не можете понять, как ее отменить, обращайтесь к рекомендациям в приложении В. Не стесняйтесь обращаться за помощью: все остальные учились строить проекты и задавали те же вопросы, которые возникнут и у вас, так что вы наверняка найдете кого-нибудь, кто согласится помочь. Решение всех возникающих проблем поможет развивать ваши навыки до того момента, когда вы начнете строить содержательные, надежные проекты и отвечать на вопросы других людей.
Django использует значение настройки SECRET_KEY из файла settings.py для реализации некоторых протоколов безопасности. В нашем проекте файл настроек был закреплен с включением настройки SECRET_KEY. Для учебного проекта этого достаточно, но на реальном сайте с настройкой SECRET_KEY следует действовать более осторожно. Если вы строите проект, предназначенный для реальной эксплуатации, обязательно изучите вопрос, как повысить безопасность настройки SECRET_KEY.
Очень полезно многократно отработать процесс развертывания на одном проекте или серии малых проектов, чтобы получить представление о развертывании. Однако вы должны знать, как удалить проект после развертывания. Heroku также может ограничивать количество бесплатно развернутых проектов, и загромождать учетную запись учебными проектами нежелательно. Войдите на веб-сайт Heroku (https://heroku.com/), и вы будете перенаправлены на страницу со списком проектов. Щелкните на проекте, который нужно удалить; открывается новая страница с информацией о проекте. Щелкните на ссылке Settings, прокрутите список и найдите ссылку для удаления проекта. Отменить удаление не удастся, поэтому Heroku предложит подтвердить удаление, для чего вам нужно будет вручную ввести имя проекта. Если вы предпочитаете работать в терминальном режиме, проект также можно удалить командой destroy:
(ll_env)learning_log$ heroku apps:destroy --app имя_приложения
Здесь имя_приложения — имя вашего проекта (например, afternoon-meadow-2775 или learning-log, если проект был переименован). Вам также будет предложено снова ввести имя проекта, чтобы подтвердить удаление.
Примечание
При удалении проекта на Heroku с локальной версией проекта ничего не происходит. Если никто не использовал ваш развернутый проект и вы просто отрабатываете процесс развертывания, ничто не мешает вам удалить свой проект на Heroku и развернуть его заново.
Упражнения
20-3. Блог в Интернете: разверните проект Blog, над которым вы работали ранее, на сервере Heroku. Проследите за тем, чтобы настройка DEBUG имела значение False, и измените настройку ALLOWED_HOSTS, чтобы развернутая копия была абсолютно безопасной.
20-4. Больше 404: функцию get_object_or_404() также следует применять в представлениях new_entry() и edit_entry(). Внесите изменения, протестируйте их на URL-адресе вида http://localhost:8000/new_entry/99999/ и убедитесь в том, что при этом выводится ошибка 404.
20-5. Расширенное приложение Learning Log: добавьте простую функцию в Learning Log (например, вывод расширенной информации о проекте на домашней странице) и отправьте изменение в развернутую копию. Затем попробуйте внести более сложное изменение — например, чтобы пользователь мог назначить тему общедоступной. Для этого в модель Topic добавляется атрибут с именем public (по умолчанию он должен быть равен False), а на страницу new_topic — элемент формы, позволяющий превратить личную тему в общедоступную. После этого проведите миграцию проекта и переработайте файл views.py, чтобы любая общедоступная тема также была видимой и для пользователей, не прошедших аутентификацию. Не забудьте провести миграцию базы данных после отправки изменений на Heroku.
В этой главе вы узнали, как придать вашему проекту простой, но профессиональный внешний вид при помощи библиотеки Bootstrap и приложения django-bootstrap3. При использовании Bootstrap выбранные вами стили будут работать одинаково практически на всех устройствах, используемых для работы с вашим проектом.
Вы узнали о шаблонах Bootstrap и использовали шаблон Static top navbar для создания простого оформления Learning Log. Вы научились использовать элемент jumbotron для визуального выделения сообщений домашней страницы и узнали, как организовать единое стилевое оформление всех страниц на сайте.
В последней части проекта вы узнали, как развернуть проект на серверах Heroku, чтобы с ним мог работать любой желающий. Вы создали учетную запись Heroku и установили инструменты, упрощающие процесс развертывания. Вы использовали Git для закрепления рабочего проекта в репозитории и отправили репозиторий на серверы Heroku. Наконец, вы узнали, как защитить приложение, включив режим DEBUG=False на работающем сервере.
Итак, работа над Learning Log закончена, и вы можете взяться за построение собственных проектов. Начните с простых приложений и убедитесь в том, что приложение заработало, прежде чем наращивать сложность. Пусть ваше обучение будет интересным — и удачи с вашими собственными проектами!