Ось твій урок. Приготуйся, сьогодні ми змінимо те, як ти сприймаєш веб-запити!
🎓 Урок: Асинхронність та Django Channels (ASGI)
Привіт, світе! 👋 Радий бачити вас на цьому занятті.
Сьогодні ми переступимо поріг класичного Django і увійдемо у світ, де все відбувається миттєво. Ми поговоримо про асинхронність та магію Django Channels.
1. 🔥 Вступ: Коли "F5" вже не рятує
Уявіть собі, що ви пишете месенджер. Найпростіший чат. Ви — користувач А, ваш друг — користувач Б.
Ви відправляєте повідомлення: "Привіт, як справи?". Ваш сервер приймає його, зберігає в базі даних і каже вам: "Окей, збережено".
Але що бачить ваш друг? Нічого. Поки він не оновить сторінку (не натисне F5), він не дізнається, що ви йому написали.
❓ Питання до вас: Чи зручно було б користуватися Telegram або Instagram, якби вам доводилося оновлювати екран щосекунди, щоб перевірити нові повідомлення? Звісно, ні. Це було б жахливо.
У чому проблема? Класичний Django працює за схемою HTTP: Запит → Відповідь. Клієнт запитав — сервер відповів — зв'язок розірвався. Сервер не може сам "постукати" до клієнта і сказати: "Гей, тут тобі лист прийшов!".
Аналогія: Уявіть, що ви чекаєте важливий лист. * Синхронний підхід (HTTP): Ви ходите до поштової скриньки кожні 5 хвилин. Пусто. Пусто. Пусто. Ви втомлюєтесь. * Асинхронний підхід (WebSockets): Поштар (сервер) дзвонить вам у двері тільки тоді, коли лист реально прийшов. Ви займаєтесь своїми справами, а реагуєте лише на дзвінок.
Ось для цього "дзвінка у двері" нам і потрібні WebSockets та Django Channels. Без них сучасний Real-time веб (чати, сповіщення, живі графіки, ігри) просто неможливий.
2. 🧠 Теоретична база: Як це працює "під капотом"
Щоб зрозуміти Channels, треба зрозуміти дві абревіатури: WSGI та ASGI.
1. WSGI (Синхронний Django)
Це старий добрий стандарт. Він працює лінійно: один процес обробляє один запит за раз. Якщо запит довгий (наприклад, чекаємо відповідь від іншого API), сервер просто "зависає" і чекає. Це як черга в касу супермаркету: поки касир не проб’є товар бабусі попереду, ви стоїте.
2. ASGI (Асинхронний Django + Channels)
Це новий стандарт (Asynchronous Server Gateway Interface). Він дозволяє Django обробляти багато подій одночасно, не чекаючи завершення попередніх.
Що треба запам'ятати (обов'язково):
* WebSockets: Це постійний "тунель" між браузером і сервером. Він відкритий увесь час. Дані можуть летіти туди й назад у будь-який момент.
* Consumer (Споживач): В Channels це аналог View (відображення). Але якщо View обробляє HTTP-запит, то Consumer обробляє повідомлення з веб-сокету.
* Channel Layers (Шари каналів): Це як "поштова служба" всередині Django. Вона дозволяє різним користувачам "спілкуватися" між собою (наприклад, переслати повідомлення з кімнати "Чат А" всім, хто в ній сидить).
Інтуїтивне розуміння: Django Channels перетворює ваш сервер з "бібліотекаря", який видає книги по одній, на "диспечера таксі", який одночасно слухає рацію, приймає замовлення і відправляє машини, не припиняючи розмову ні на секунду.
3. 🧪 Приклади (від простого до реального)
Щоб Channels запрацювали, нам треба встановити daphne (ASGI-сервер) та налаштувати asgi.py замість wsgi.py. Але припустімо, що налаштування вже зроблені.
Приклад 1: Ехо-бот (Найпростіший)
Питання: Якщо я надішлю серверу слово "Hello", що має зробити найпростіший WebSocket сервер? Правильно, він має відправити це саме слово назад.
# consumers.py
import json
from channels.generic.websocket import WebsocketConsumer
class EchoConsumer(WebsocketConsumer):
# 1. Клієнт постукав (connect)
def connect(self):
self.accept() # Ми кажемо: "Заходь!"
# 2. Клієнт щось надіслав (receive)
def receive(self, text_data):
data = json.loads(text_data)
message = data['message']
# 3. Відправляємо назад
self.send(text_data=json.dumps({
'message': f"Ти сказав: {message}"
}))
Пояснення: Це синхронний код всередині Channels. Ми просто прийняли з'єднання і на будь-яку фразу відповідаємо нею ж.
Приклад 2: Чат-кімната (Реальний сценарій)
Тепер складніше. Юзер А пише повідомлення, а Юзер Б має його побачити. Тут нам потрібні групи.
# consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = "general_chat"
self.room_group_name = f"chat_{self.room_name}"
# Додаємо поточного юзера в групу "general_chat"
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Видаляємо з групи при відключенні
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Отримуємо повідомлення від WebSocket (від клієнта)
async def receive(self, text_data):
data = json.loads(text_data)
message = data['message']
# Відправляємо повідомлення ВСІЙ ГРУПІ (а не тільки собі)
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message', # Це назва методу-обробника нижче
'message': message
}
)
# Цей метод викликається, коли група отримує повідомлення
async def chat_message(self, event):
message = event['message']
# Відправляємо фінальне повідомлення своєму клієнту (в браузер)
await self.send(text_data=json.dumps({
'message': message
}))
Що тут відбулося? 1. Клієнт підключився → ми додали його в "кімнату" (Layer). 2. Клієнт написав → ми переслали це в Layer. 3. Layer розіслав це всім учасникам кімнати. 4. Кожен учасник отримав це повідомлення у свій браузер.
4. 🛠 Практична частина
Час кодити! Не бійтеся помилятися — саме так ми вчимося.
Завдання 1: Ping-Pong Створіть Consumer, який при отриманні повідомлення "Ping" відповідає "Pong". На будь-яке інше повідомлення відповідає "I don't understand".
Завдання 2: Живий годинник (JavaScript + Django)
Напишіть JavaScript код на стороні клієнта, який з’єднується з вашим сокетом і виводить у консоль браузера все, що приходить.
Підказка: const chatSocket = new WebSocket('ws://...' + window.location.host + '/ws/chat/');
Завдання 3: "А що, якщо..." (Обробка розриву) Змініть код так, щоб коли користувач виходить з чату (disconnect), у консоль сервера виводилося повідомлення: "Користувач покинув чат :( ".
Завдання 4: Реальний кейс (Сповіщення)
Уявіть, що у вас є модель Order (Замовлення). Коли статус замовлення змінюється (наприклад, через адмінку Django), клієнт має миттєво побачити попап "Ваша піца готова!".
Підказка: Вам знадобляться Django Signals (post_save), щоб викликати відправку повідомлення в Channels Layer.
Міні-кейс для самостійного вирішення: Створіть просту гру "Хто перший натисне кнопку". Сервер надсилає сигнал "START". Хто перший надішле відповідь — той переміг, а іншим приходить "Ви програли".
5. 💡 Мислення як у розробника
А тепер послухайте уважно. Ось де новачки ламають собі зуби.
⚠️ Типова помилка №1: Блокування потоку
Ви пишете async def, але всередині використовуєте синхронні функції, які довго "думають" (наприклад, time.sleep(5) або важкий запит до БД без адаптера).
Результат: Весь ваш асинхронний сервер зупиняється і чекає. Інші користувачі в шоці.
Як думає профі: "Якщо я в асинхронному коді, я маю використовувати await asyncio.sleep(5), а для роботи з БД — спеціальні асинхронні методи Django ORM (await User.objects.aget(...)) або обгортку database_sync_to_async".
⚠️ Типова помилка №2: WebSockets для всього
Новачки, дізнавшись про Channels, намагаються переписати на них весь сайт. Порада: Не треба. WebSockets — це дорого для сервера (тримає з'єднання відкритим). Використовуйте їх тільки там, де потрібен реал-тайм. Для завантаження списку статей звичайний HTTP — все ще король.
⚠️ Типова помилка №3: Забути про Redis
У прикладах ми використовуємо пам'ять (InMemoryChannelLayer). Це ок для тесту. Але якщо ви запустите це в продакшн з декількома серверами — користувачі на Сервері 1 не побачать повідомлень від Сервера 2.
Рішення: Завжди використовуйте Redis як бекенд для Channel Layers у реальних проектах.
6. 🧩 Підсумок
Отже, що ми сьогодні зробили? 1. Зрозуміли, чому натискати F5 — це минуле століття. 2. Розібрали різницю між WSGI (синхронним) та ASGI (асинхронним). 3. Навчилися створювати Consumers, які тримають постійний зв'язок з клієнтом. 4. Зрозуміли концепцію Groups, щоб робити чат-кімнати.
Тепер ви вмієте: Створювати інтерактивні веб-додатки, які реагують на події миттєво. Ви більше не обмежені схемою "запит-відповідь".
🚀 Що далі? Асинхронність — це круто, але що робити, якщо треба виконати задачу, яка займає 10 хвилин (обробка відео, експорт звіту)? Браузер не буде чекати. На наступному уроці ми познайомимося з Celery — вашим найкращим другом для фонових задач.
А поки що — вперед до коду! Якщо щось не працює — читайте логи, вони ваші найкращі друзі. Щасти! 👨💻👩💻