- PVSM.RU - https://www.pvsm.ru -

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 1

В предыдущей части [1] мы частично разобрали шаблон для нашего блога, выбрали виртуальную машину [2] и запустили на ней нативный веб-сервер Django. Однако он предназначен только для тестирования и запуска приложений во время разработки. Для обработки запросов в продакшене нужно настроить Nginx и WSGI Gunicorn. В этой статье показываем, как это сделать.

Добавляем новые фичи


Перед запуском блога в продакшен лучше убедиться, что необходимая функциональность реализована. В первой части мы собрали лишь базовые Django-приложения — users [3] и blog [4]. Но остальные фичи мы пока не добавили — пора это исправить.

Ниже разбираем шаблоны для создания опросов и тестов в блоге на Django. Делайте форк исходного кода [5], модифицируйте и предлагайте свои улучшения!

Приложение polls

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 2

В приложении polls [6] сосредоточена вся функциональность, связанная с проведением опросов. Чтобы его создать, нужно запустить управляющий скрипт manage.py и зарегистрировать новое приложение.

manage.py startapp polls

Также важно зарегистрировать новое приложение в корневой директории проекта, в конфигурационном файле settings.py.

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 3

Чтобы пользователь мог подключиться к приложению, нужно настроить маршрутизацию — перейти в корневую директорию проекта и зарегистрировать пути в конфигурационном файле urls.py.

path('', include('polls.urls')) 

Собственные маршруты приложения polls будут храниться в файле users/urls.py — их можно найти и модифицировать по ссылке [7].

Приложение 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 [8]:

  • question.html [9] — выводит вопрос с вариантами ответа,
  • questions.html [10] — обеспечивает постраничный вывод всех созданных опросов,
  • results.html [11] — показывает результаты голосования.

Метод user_voted класса Question [12] проверяет, принимал ли пользователь участие в опросе (проголосовать можно только один раз). Логика проведения опроса описана в polls/views.py [13]. Функция vote предусматривает случаи, когда пользователь не выбрал ни один вариант ответа, и когда он уже принимал участие в опросе.

Приложение quizzes

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 4

Приложение quizzes [14]обеспечивает создание и обработку двух типов опросов — с одним и с несколькими правильными ответами. Аналогичным образом создаем его, регистрируем в settings.py и инициализируем в urls.py корневой директории. Маршруты самого блога находятся в quizzes/urls.py [15], а шаблоны — в quizzes/templates/quizzes [16].

Модуль опросов использует четыре шаблона:

  • display.html [17] — показывает вопрос и варианты ответа,
  • quizzes.html [18] — отвечает за постраничный вывод созданных тестов,
  • results.html [19] — выводит результаты прохождения теста.
  • partial.html [20] — обеспечивает — совместно с JS-скриптом в файле display.html — вывод фидбека без перезагрузки страницы,

Класс Question в quizzes/models.py [21] содержит два вспомогательных метода:

  • get_answers — возвращает правильные ответы, в зависимости от типа вопроса,
  • user_can_answer — определяет, отвечал ли пользователь на этот вопрос ранее.

Функции представления в quizzes/views.py [22] отвечают за последовательный вывод вопросов теста и оценку ответов пользователя. Сохранение статистики по правильным и неверным ответам реализовано с помощью класса 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

Готово — теперь можно отправить блог в продакшен!

Обновление файлов на сервере

В предыдущей части мы уже арендовали виртуальный сервер [23] с гибкой производительностью ядра и загрузили код проекта. Теперь его необходимо обновить — задеплоить новые приложения polls и quizzes. Для этого нужно запушить их в репозиторий на GitHub, а после — сделать git pull на стороне сервера.

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 5

Настройка Nginx и Gunicorn


При обслуживании Django-приложения Nginx выступает в качестве обратного прокси-сервера: отвечает за обработку входящих запросов на 80 порт и переадресацию их в Gunicorn.

Gunicorn же исполняет роль сервера приложений: отвечает за запуск Django, обработку запросов и генерацию ответов. В результате система работает по такой схеме:

  1. Клиент отправляет запрос на сервер Nginx,
  2. Nginx пересылает (проксирует) запрос в Gunicorn с помощью директивы proxy pass?
  3. Gunicorn передает пользовательский запрос в Django,
  4. Django обрабатывает запрос, генерирует ответ и отправляет в Gunicorn,
  5. Gunicorn отправляет результат в Nginx, который перенаправляет его клиенту.

При такой конфигурации Nginx обеспечивает быструю и эффективную обработку запросов, а Gunicorn позволяет запускать несколько процессов для параллельной обработки запросов и отвечает за автоматический перезапуск и плавное завершение рабочих процессов.

Установка и тестирование Gunicorn

В установке нет ничего сложного: достаточно активировать виртуальное окружение и установить необходимые зависимости.

source blogitenv/bin/activate
pip install gunicorn

Проверьте и при необходимости измените настройки config/settings.py [24], как было показано в предыдущей части. А после создайте суперпользователя и базу данных и соберите статические файлы.

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-приложения.

Возможно, эти тексты тоже вас заинтересуют:

Простая процедурная генерация мира, или Шумы Перлина на Python [25]
«Карманный синоптик за час». Пишем Telegram-бота для мониторинга погоды на Python [26]
Проверяем ветхозаветную историю происхождения человечества от Адама и Евы с помощью популяционной модели [27]

Настройка 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 должен их запустить.

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 6

Проксирование запросов

Последним этапом нужно перейти в настройки сервера 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-сертификат.

Создаем блог на Django с опросами и тестами. Краткая инструкция. Часть 2 - 7

Мы все еще разработали лишь малую часть функциональности блога. Например, можно настроить восстановление пароля с помощью email-сообщений с помощью почтового сервиса Selectel [28] (на время беты — бесплатно).

А какие бы фичи добавили вы? Поделитесь своими идеями в комментариях!

Автор:
Seleditor

Источник [29]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/385103

Ссылки в тексте:

[1] предыдущей части: https://habr.com/ru/companies/selectel/articles/735556/

[2] виртуальную машину: https://selectel.ru/services/cloud/servers/?utm_source=habr.com&utm_medium=referral&utm_campaign=cloud_article_gjango_310523_content

[3] users: https://github.com/natkaida/blogit/tree/main/users

[4] blog: https://github.com/natkaida/blogit/tree/main/blog

[5] форк исходного кода: https://github.com/natkaida/blogit

[6] polls: https://github.com/natkaida/blogit/tree/main/polls

[7] ссылке: https://github.com/natkaida/blogit/blob/main/polls/urls.py

[8] polls/emplates/polls: https://github.com/natkaida/blogit/tree/main/polls/templates/polls

[9] question.html: https://github.com/natkaida/blogit/blob/main/polls/templates/polls/question.html

[10] questions.html: https://github.com/natkaida/blogit/blob/main/polls/templates/polls/questions.html

[11] results.html: https://github.com/natkaida/blogit/blob/main/polls/templates/polls/results.html

[12] класса Question: https://github.com/natkaida/blogit/blob/main/polls/models.py

[13] polls/views.py: https://github.com/natkaida/blogit/blob/main/polls/views.py

[14] quizzes : https://github.com/natkaida/blogit/tree/main/quizzes

[15] quizzes/urls.py: https://github.com/natkaida/blogit/blob/main/quizzes/urls.py

[16] quizzes/templates/quizzes: https://github.com/natkaida/blogit/tree/main/quizzes/templates/quizzes

[17] display.html: https://github.com/natkaida/blogit/blob/main/quizzes/templates/quizzes/display.html

[18] quizzes.html: https://github.com/natkaida/blogit/blob/main/quizzes/templates/quizzes/quizzes.html

[19] results.html: https://github.com/natkaida/blogit/blob/main/quizzes/templates/quizzes/results.html

[20] partial.html: https://github.com/natkaida/blogit/blob/main/quizzes/templates/quizzes/partial.html

[21] quizzes/models.py: https://github.com/natkaida/blogit/blob/main/quizzes/models.py

[22] quizzes/views.py: https://github.com/natkaida/blogit/blob/main/quizzes/views.py

[23] виртуальный сервер: https://selectel.ru/services/cloud/sharedline/?utm_source=habr.com&utm_medium=referral&utm_campaign=cloud_article_gjango_310523_content

[24] config/settings.py: https://github.com/natkaida/blogit/blob/main/config/settings.py

[25] Простая процедурная генерация мира, или Шумы Перлина на Python: https://habr.com/ru/companies/selectel/articles/731506/

[26] «Карманный синоптик за час». Пишем Telegram-бота для мониторинга погоды на Python: https://habr.com/ru/companies/selectel/articles/734194/

[27] Проверяем ветхозаветную историю происхождения человечества от Адама и Евы с помощью популяционной модели: https://habr.com/ru/companies/selectel/articles/729828/

[28] почтового сервиса Selectel: https://selectel.ru/services/additional/smtp/?utm_source=habr.com&utm_medium=referral&utm_campaign=cloud_article_gjango_310523_content

[29] Источник: https://habr.com/ru/companies/selectel/articles/738942/?utm_source=habrahabr&utm_medium=rss&utm_campaign=738942