Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2
Рассказываем, как создать многофункциональный блог и настроить обработку веб-сервер на базе Nginx и Gunicorn.
Введение
В предыдущей части мы разобрали шаблон для нашего блога, выбрали виртуальную машину и запустили на ней нативный веб-сервер Django. Однако он предназначен только для тестирования и запуска приложений во время разработки. Для обработки запросов в продакшене нужно настроить Nginx и WSGI Gunicorn. В этой статье коллеги из Proglib показывают, как это сделать.
Ознакомиться с блогом можно по ссылке.
Добавляем новые фичи
Перед запуском блога в продакшен лучше убедиться, что необходимая функциональность реализована. В первой части мы собрали лишь базовые Django-приложения — users и blog. Но остальные фичи мы пока не добавили — пора это исправить.
Ниже разбираем шаблоны для создания опросов и тестов в блоге на Django. Делайте форк исходного кода, модифицируйте и предлагайте свои улучшения!
Приложение polls
В приложении polls сосредоточена вся функциональность, связанная с проведением опросов. Чтобы его создать, нужно запустить управляющий скрипт manage.py и зарегистрировать новое приложение.
manage.py startapp polls
Также важно зарегистрировать новое приложение в корневой директории проекта, в конфигурационном файле settings.py.
Чтобы пользователь мог подключиться к приложению, нужно настроить маршрутизацию — перейти в корневую директорию проекта и зарегистрировать пути в конфигурационном файле urls.py.
path('', include('polls.urls'))
Собственные маршруты приложения polls будут храниться в файле users/urls.py — их можно найти и модифицировать по ссылке.
Приложение polls визуализирует результаты опросов с помощью библиотеки Chart.j: логика построения диаграмм описана в функции results
.
def results(request, question_id):
profile = request.user.profile
question = get_object_or_404(Question, pk=question_id)
labels = []
data = []
votes = question.choice_set.select_related('question').all()
for item in votes:
labels.append(item.name)
data.append(item.votes)
context = { 'question': question,
'profile': profile,
'labels': labels,
'data': data}
return render(request, 'polls/results.html', context)
По возможности выносите логику валидаторов из views в отдельные модули.
Все необходимые шаблоны находятся в директории polls/emplates/polls:
- question.html — выводит вопрос с вариантами ответа,
- questions.html — обеспечивает постраничный вывод всех созданных опросов,
- results.html — показывает результаты голосования.
Метод user_voted класса Question проверяет, принимал ли пользователь участие в опросе (проголосовать можно только один раз). Логика проведения опроса описана в polls/views.py. Функция vote предусматривает случаи, когда пользователь не выбрал ни один вариант ответа, и когда он уже принимал участие в опросе.
Приложение quizzes
Приложение quizzes обеспечивает создание и обработку двух типов опросов — с одним и с несколькими правильными ответами. Аналогичным образом создаем его, регистрируем в settings.py и инициализируем в urls.py корневой директории. Маршруты самого блога находятся в quizzes/urls.py, а шаблоны — в quizzes/templates/quizzes.
Модуль опросов использует четыре шаблона:
- display.html — показывает вопрос и варианты ответа,
- partial.html — обеспечивает — совместно с JS-скриптом в файле display.html — вывод фидбека без перезагрузки страницы,
- quizzes.html — отвечает за постраничный вывод созданных тестов.
- results.html — выводит результаты прохождения теста.
Класс Question
в quizzes/models.py содержит два вспомогательных метода:
get_answers
— возвращает правильные ответы, в зависимости от типа вопроса,user_can_answer
— определяет, отвечал ли пользователь на этот вопрос ранее.
Функции представления в quizzes/views.py отвечают за последовательный вывод вопросов теста и оценку ответов пользователя. Сохранение статистики по правильным и неверным ответам реализовано с помощью класса Result
. При этом обновление значений в базе данных проводится с помощью объекта F
.
result, created = Result.objects.get_or_create(user=request.user,
quiz=quiz)
if is_correct is True:
result.correct = F('correct') + 1
else:
result.wrong = F('wrong') + 1
Готово — теперь можно отправить блог в продакшен!
Обновление файлов на сервере
В предыдущей части мы уже арендовали виртуальный сервер с гибкой производительностью ядра и загрузили код проекта. Теперь его необходимо обновить — задеплоить новые приложения polls и quizzes. Для этого нужно запушить их в репозиторий на GitHub, а после — сделать git pull
на стороне сервера.
Облачные серверы с гибкой производительностью ядра vCPU
Настройка Nginx и Gunicorn
При обслуживании Django-приложения Nginx выступает в качестве обратного прокси-сервера: отвечает за обработку входящих запросов на 80 порт и переадресацию их в Gunicorn.
Gunicorn же исполняет роль сервера приложений: отвечает за запуск Django, обработку запросов и генерацию ответов. В результате система работает по такой схеме:
- клиент отправляет запрос на сервер Nginx,
- Nginx пересылает (проксирует) запрос в Gunicorn с помощью директивы proxy pass,
- Gunicorn передает пользовательский запрос в Django,
- Django обрабатывает запрос, генерирует ответ и отправляет в Gunicorn,
- Gunicorn отправляет результат в Nginx, который перенаправляет его клиенту.
При такой конфигурации Nginx обеспечивает быструю и эффективную обработку запросов, а Gunicorn позволяет запускать несколько процессов для параллельной обработки запросов и отвечает за автоматический перезапуск и плавное завершение рабочих процессов.
Установка и тестирование Gunicorn
В установке нет ничего сложного: достаточно активировать виртуальное окружение и установить необходимые зависимости.
source blogitenv/bin/activate
pip install gunicorn
Проверьте и при необходимости измените настройки config/settings.py, как было показано в предыдущей части. А после создайте суперпользователя и базу данных и соберите статические файлы.
python3 manage.py migrate
python3 manage.py createsuperuser
python3 manage.py collectstatic
Теперь можно протестировать работу Gunicorn, запустив его в связке с Django-приложением.
gunicorn --bind 0.0.0.0:8000 config.wsgi
Все работает: к блогу можно подключиться по IP-адресу через 8000-порт. Но сейчас веб-сервер запускается вручную. В продакшене это должно происходить автоматически — нужно настроить Gunicorn и Nginx для совместного обслуживания Django-приложения.
Настройка Gunicorn
Для автоматизации запуска Django-приложения сначала нужно создать конфигурационные файлы gunicorn.socket и gunicorn.service. Начнем с первого:
sudo nano /etc/systemd/system/gunicorn.socket
[Unit]
Description=Gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
Содержимое файла gunicorn.socket
Теперь создадим gunicorn.service и укажем на виртуальное окружение, которое должен запускать веб-сервер.
sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/root/blogit
ExecStart=/root/blogitenv/bin/gunicorn \
--access-log file - \
--workers 5 \
--bind unix:/run/gunicorn.sock \
config.wsgi:application
[Install]
WantedBy=multi-user.target
Содержимое файла gunicorn.service
Теперь можно запустить Gunicorn и проверить его статус.
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
sudo systemctl start gunicorn.service
sudo systemctl enable gunicorn.service
sudo systemctl status gunicorn
Правильно настроенный сервер вернет статус running и выведет информацию о workers — параллельно запущенных инстансах Django-приложения. В конфигурационном файле gunicorn.service мы зарегистрировали пять «воркеров» — Gunicorn должен их запустить.
Проксирование запросов
Последним этапом нужно перейти в настройки сервера Nginx и настроить проксирование запросов с 80 порта на Gunicorn.
sudo nano /etc/nginx/sites-available/blogit
server {
listen 80;
server_name 94.26.224.162;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location = /favicon.ico {
alias /root/blogit/static/favicon.ico;
}
location /static/ {
alias /root/blogit/static/;
}
location /media/ {
alias /root/blogit/media/;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
Содержимое файла /etc/nginx/sites-available/blogit. В качестве server_name укажите публичный IP-адрес своей виртуальной машины.
Чтобы применить конфигурации, свяжите директивы Nginx — sites-available и sites-enabled — с помощью символической ссылки:
sudo ln -s /etc/nginx/sites-available/blogit /etc/nginx/sites-enabled/
Обратите внимание: если в конфигурационных файлах в дальнейшем будет обнаружена ошибка, ссылку необходимо создать заново, но уже с флагом -sf.
sudo ln -sf /etc/nginx/sites-available/blogit /etc/nginx/sites-enabled/
sudo systemctl reload nginx
Кроме того, если вы разворачиваете проект не под root, нужно назначить права доступа для директорий static и media.
chown -R django_user:www-data /var/www/blogit/static/
chown -R django_user:www-data /var/www/blogit/media
На этом настройка Nginx и Gunicorn завершена. Теперь вы можете открыть сайт через браузер. При желании вы можете арендовать доменное имя и сгенерировать SSL-сертификат.
Заключение
Мы все еще разработали лишь малую часть функциональности блога. Например, можно бесплатно протестировать почтовый сервис Selectel и настроить восстановление пароля с помощью email-сообщений. Делайте форк проекта и предлагайте свои улучшения!