Разработка канбан-доски на Django, DRF и Alpine.js
Рассказываем, как создать канбан-доску, используя популярные фреймворки.
Введение
Рассказываем, как создать собственный вариант популярного приложения и развернуть его с использованием Nginx и Gunicorn на сервере под управлением Ubuntu 20.04.
Сервер с гибкой производительностью для pet-проектов
С чего начнем
Мы разработаем и запустим онлайн-версию классической канбан-доски с 4-мя колонками, которые соответствуют различным стадиям выполнения задач. Карточки можно перетаскивать из одной колонки в другую, редактировать и удалять без перезагрузки страницы:
Цветовую схему и фоновое изображение хедера можно изменить без обновления страницы:
Канбан максимально использует функциональность Django — управление базой данных, формы, шаблоны, систему аутентификации и авторизации. Асинхронная передача данных и CRUD без перезагрузки доски реализованы с помощью API на Django REST Framework.Фронтенд канбана написан на Alpine.js — это минималистичный JS-фреймворк, который по синтаксису очень похож на Vue.js, а по функциональности на jQuery. Дизайн сделан на CSS-фреймворке Tailwind, а для HTTP-запросов к бэкенду используется библиотека Axios. Весь код для проекта находится в репозитории Kanban.
Бэкенд и API
Модель базы данных board/models.py
Поскольку в канбане фиксированное число колонок, можно обойтись одной моделью Task с полем models.TextChoices. Если нужно дать пользователям возможность создавать неограниченное количество колонок с произвольными именами (как в Trello), потребуется модель Board, с которой модель Task будет связана при помощи ForeignKey. Сериализатор в этом случае будет вложенным.
Представления board/views.py
Декоратор @login_required обеспечивает перенаправление на страницу входа/регистрации для неавторизованных посетителей. Функция def home(request) передает в шаблон index.html все существующие записи пользователя в удобном формате для сортировки задач по статусам.Вся функциональность, связанная с регистрацией, авторизацией и выходом, реализована средствами Django и дополнительного модуля для управления формами django-crispy-forms. Crispy Forms обеспечивают автоматическую валидацию данных. Дизайн для форм предоставляет модуль crispy-tailwind.
API
Реализация API на Django REST Framework выглядит максимально просто и лаконично:
- Сериализаторы обеспечивают преобразование информации из нужных полей базы данных для использования на фронтенде и конвертируют получаемые с фронтенда данные для записи в базу.
- Универсальные представления на основе классов ListTask и DetailTask предоставляют всю необходимую CRUD функциональность — создание, редактирование и удаление записей.
- Фильтр owner=user в get_queryset(self) предоставляет пользователям доступ только к своим собственным записям.
- Схемы SessionAuthentication и IsAuthenticated гарантируют, что операции с данными по API могут совершать только авторизованные пользователи.
Настройки settings.py
В INSTALLED_APPS необходимо указать формы crispy_forms
и набор стилей для них crispy_tailwind:
INSTALLED_APPS = [
'board.apps.BoardConfig',
'rest_framework',
'api.apps.ApiConfig',
'crispy_forms',
'crispy_tailwind',
]
Фронтенд
Клиентская часть приложения также в значительной мере полагается на встроенные возможности Django и его дополнительных модулей: django-crispy-forms, crispy-tailwind и messages. Фронтенд Канбана состоит из следующих шаблонов:
- base.html — базовый шаблон с общими настройками для всех остальных страниц. Здесь подключаются Alpine.js, Axios, Tailwind и набор иконок Unicons.
- index.html — главный шаблон, в котором содержится вся клиентская функциональность приложения.
- login.html — вход на сайт.
- messages.html — шаблоны для получения с бэкенда сообщений об успешных (и ошибочных) действиях пользователя.
- register.html — регистрация на сайте.
- settings.html — настройки пользовательского интерфейса (цветовая схема и фоновое изображение). Сведения об этих настройках хранятся в localStorage браузера.
Шаблон index.html получает данные с бэкенда при загрузке страницы:
const tasksFromDjango = {{ tasks | safe }};
Alpine.js распределяет задачи по соответствующим колонкам:
<template x-for="(t, taskIndex) in tasks.filter(t => t.boardName === board)" :key="taskIndex">
Обращения к API для добавления, удаления и редактирования записей реализованы в addTask, removeTask и updateTask; перемещение задания в другую колонку — в onDrop(event, boardName).
Развертывание приложения на сервере с ОС Ubuntu
Последовательность действий при развертывании готового проекта состоит из следующих шагов:
- выбор конфигурации сервера, обновление ОС,
- установка глобальных пакетов и модулей,
- создание виртуального окружения,
- клонирование репозитория проекта и установка локального ПО,
- корректировка settings.py для продакшена,
- создание базы данных и суперпользователя,
- связывание Gunicorn с wsgi.py,
- настройка конфигурации и запуск Gunicorn,
- настройка и запуск обратного прокси Nginx,
- получение сертификата Let’s Encrypt.
Рассмотрим эти шаги подробнее.
Конфигурация сервера
В панели управления Selectel откройте раздел «Облачная платформа», выберите «Серверы». Самый оптимальный вариант для развертывания нашего проекта — Shared Line:
Не забудьте выбрать опцию «Новый плавающий IP-адрес»: по этому адресу приложение будет доступно в интернете:
Конфигурация нашего сервера выглядит так:
Обновление ОС и установка глобального ПО
Начинаем работу с обновления Ubuntu и установки нужных пакетов:
sudo apt update
sudo apt install git ufw python3-pip python3-dev nginx
Создание виртуального окружения и клонирование репозитория с GitHub
Установим virtualenv:
sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv
Создадим директорию для проекта:
mkdir myproject
cd myproject
Создадим виртуальное окружение для проекта:
virtualenv myprojectenv
Активируем окружение:
source myprojectenv/bin/activate
В результате в начале командной строки будет отображаться (myprojectenv).
Теперь скопируем файлы проекта из GitHub-репозитория на сервер:
git clone https://github.com/natkaida/kanban.git
Установка Gunicorn и production-настройки
Перейдем в корневую директорию проекта, установим зависимости и Gunicorn:
cd kanban
pip install -r requirements.txt
pip install gunicorn
Создадим файл local_settings.py по этому примеру:
nano kanban/kanban/local_settings.py
Нужно убедиться, что значение DEBUG равно False, и прописать допустимые IP — локальный и внешний, в нашем случае это 95.213.229.177:
ALLOWED_HOSTS = ['95.213.229.177', 'localhost']
Кроме этого, нужно вставить соответствующее значение в SECRET_KEY, например:
SECRET_KEY = '!_u(2(^lpy&-2x$e%!k_u_gir0hg)q&5nurj00*gq0s4sseuav'
Теперь можно создать базу данных, сделать аккаунт администратора и собрать статические файлы в папку static:
python3 manage.py migrate
python3 manage.py createsuperuser
python3 manage.py collectstatic
Назначьте права для папки static:
chown www-data myproject/kanban/static
Добавьте порт 8000 в исключения фаервола:
sudo ufw allow 8000
Теперь можно запустить проект на собственном сервере Django:
python3 manage.py runserver 0.0.0.0:8000 --insecure
Параметр —insecure нужен для того, чтобы Django «увидел» статические файлы в папке static — после переключения статуса DEBUG на False он уже не занимается статикой по умолчанию. В дальнейшем работать со статическими файлами будет другой сервер. Мы установим его чуть позже. После запуска сервера сайт будет доступен по адресу http:// 95.213.229.177:8000:
Для входа в панель администрирования достаточно добавить /admin – http:// 95.213.229.177:8000/admin:
Подключение Gunicorn
Для работы Django-приложения в продакшене необходим надежный WSGI-сервер. В качестве такого сервера мы будем использовать Gunicorn:
cd myproject
gunicorn --bind 0.0.0.0:8000 kanban.wsgi
Перезапустим сервер:
python3 manage.py runserver 0.0.0.0:8000
Сайт должен быть доступен по тому же адресу http:// 95.213.229.177:8000. В случае ошибки проверьте, установлен ли Gunicorn в виртуальное окружение проекта myprojectenv. Если все работает корректно, остановите сервер (Ctrl + C) и деактивируйте виртуальное окружение командой deactivate.
Настройка конфигурации Gunicorn
Пока что нам приходилось запускать и останавливать сервер вручную. Но при наличии соответствующих конфигурационных файлов этот процесс можно полностью автоматизировать.
Нам понадобятся два файла: gunicorn.socket и gunicorn.service. Сначала создадим файл gunicorn.socket:
sudo nano /etc/systemd/system/gunicorn.socket
Его содержимое выглядит так:
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
Нажмите Ctrl + X и подтвердите сохранение.
Теперь с помощью команды sudo nano /etc/systemd/system/gunicorn.service создайте файл gunicorn.service:
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=root
WorkingDirectory=/root/myproject/kanban
ExecStart=/root/myproject/myprojectenv/bin/gunicorn --workers 5 --bind unix:/run/gunicorn.sock kanban.wsgi:application
[Install]
WantedBy=multi-user.target
Проверить файл gunicorn.service на наличие ошибок можно этой командой:
systemd-analyze verify gunicorn.service
Если при проверке оказалось, что путь к вашему gunicorn отличается от приведенного выше, то корректный путь к файлу можно найти, если активировать виртуальное окружение и выполнить команду which gunicorn. Если ошибок нет, можно активировать сервер:
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
Проверим статус:
sudo systemctl status gunicorn.socket
Статус корректно настроенного Gunicorn выглядит так:
В случае обнаружения ошибок, после их исправления нужно перезапустить Gunicorn, иначе он будет использовать предыдущую, ошибочную конфигурацию:
systemctl daemon-reload
Настройка Nginx
Перейдем к настройке Nginx. Создайте новый файл kanban без расширения:
sudo nano /etc/nginx/sites-available/kanban
В содержимом файла необходимо указать IP, по которому будет доступен сайт, порт и путь к директории со статическими файлами:
server {
listen 80;
server_name 95.213.229.177;
location = /favicon.ico { alias /myproject/kanban/static/favicon.ico }
location /static {
root /myproject/kanban/static;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
Сохраните файл, а затем выполните приведенную ниже команду для создания символической ссылки:
sudo ln -s /etc/nginx/sites-available/kanban /etc/nginx/sites-enabled/
Чтобы проверить файл на наличие ошибок или опечаток, выполните команду:
sudo nginx -t
Если все в порядке, перезапустите Nginx:
sudo systemctl restart nginx
Осталось открыть порт 80:
sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'
Все готово — сайт доступен по адресу 95.213.229.177:
Сертификат Let’s Encrypt
Для безопасной передачи данных на сайт нужно установить SSL-сертификат. Тогда сайт будет доступен по https. Однако получить сертификат можно лишь при наличии доменного имени. Для установки certbot выполните команду:
sudo apt-get install certbot python3-certbot-nginx
Затем запустите процедуру получения сертификата:
sudo certbot certonly --nginx
На этом этапе потребуется ввести адрес электронной почты и согласиться с условиями предоставления сертификата, после чего необходимо указать доменное имя сайта. Если указать просто IP, процедура прервется:
Заключение
Деплой Django-приложения — непростая задача для начинающего разработчика. Однако процесс развертывания становится интуитивно понятным, если представить себе схему взаимодействия Nginx, Django и Gunicorn: в этой связке Nginx выступает в качестве веб-сервера, обратного прокси-сервера и отвечает за выдачу статического контента, в то время как Gunicorn играет роль сервера приложений и прослойки между Nginx и Django. Nginx не способен запускать веб-приложения на Python и переводить запросы в WSGI, поэтому он передает HTTP-запросы Gunicorn, который, в свою очередь, отправляет их в Django для взаимодействия с ORM базы данных.
При использовании настроек, приведенных в статье, Nginx и Gunicorn будут идеально дополнять друг друга и обеспечат бесперебойную работу высоконагруженного Django-приложения.