Как создать канбан-доску, используя популярные фреймворки

Разработка канбан-доски на Django, DRF и Alpine.js

Рассказываем, как создать канбан-доску, используя популярные фреймворки.

Введение

Рассказываем, как создать собственный вариант популярного приложения и развернуть его с использованием Nginx и Gunicorn на сервере под управлением Ubuntu 20.04.

С чего начнем

Мы разработаем и запустим онлайн-версию классической канбан-доски с 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-приложения.