حول المحتوى:
تعلّم كيفية بناء تطبيق دردشة في الوقت الحقيقي باستخدام Django Channels خطوة بخطوة، مع شرح آلية WebSockets، إعداد المستهلكات، وإنشاء واجهة دردشة تفاعلية.
في السنوات الأخيرة، شهدت تطبيقات الويب تطورًا كبيرًا في طبيعة التفاعل مع المستخدمين، فلم يعد الأمر مقتصرًا على صفحات ثابتة تُحدّث عند كل طلب HTTP، بل أصبح المستخدم يتوقع استجابات لحظية وتفاعلات آنية دون الحاجة إلى إعادة تحميل الصفحة. ومن بين أبرز الأمثلة على ذلك تطبيقات الدردشة، حيث يشترط المستخدمون أن تصل الرسائل فور كتابتها، وتظهر الرسائل الجديدة لدى جميع الأطراف في نفس اللحظة.
رغم أن إطار العمل Django يُعد من أكثر أطر Python شهرةً واعتمادية في بناء تطبيقات الويب، إلا أن بنيته التقليدية المبنية على WSGI لم تكن مهيأة في الأصل للتعامل مع الاتصالات المفتوحة طويلة الأمد (long-lived connections) أو البث اللحظي (real-time streaming)، وهو ما يجعل من دعم بروتوكولات مثل WebSocket تحديًا تقنيًا لا يمكن تجاوزه دون إضافة أدوات متخصصة.
هنا يأتي دور Django Channels، وهي حزمة مكملة لـ Django تُعيد تعريف طريقة معالجة الطلبات، من خلال دعم واجهة ASGI التي تسمح بتعدد البروتوكولات وفتح قنوات اتصال دائمة بين العميل والخادم. بفضل هذه الحزمة، يمكن لتطبيقات Django الحديثة أن تدير اتصالات WebSocket بكفاءة، وتتعامل مع الاتصالات غير المتزامنة بنفس مرونة وسلاسة التطبيقات المصممة من البداية لهذا الغرض.
في هذا الدليل العملي، سنقوم ببناء تطبيق دردشة بسيط يعمل في الوقت الحقيقي باستخدام Django Channels. وسنمر خطوةً بخطوة عبر جميع مراحل الإعداد، من تجهيز بيئة العمل وحتى تنفيذ واجهة المستخدم وإطلاق التطبيق التجريبي. الهدف من هذا الدليل ليس فقط إتمام تطبيق دردشة، بل فهم الآلية الكامنة وراء Django Channels، وكيف يمكن توظيفها لبناء تطبيقات تفاعلية في سيناريوهات أكثر تعقيدًا مستقبلًا.
قبل البدء في بناء أي تطبيق يعتمد على Django Channels، من الضروري إعداد بيئة العمل بشكل صحيح لضمان استقرار المشروع وتفادي المشكلات أثناء التطوير أو التشغيل. سنبدأ بإنشاء مشروع Django جديد، تثبيت الحزم المطلوبة، وضبط إعدادات ASGI التي تعد بمثابة بوابة التطبيق للتعامل مع WebSockets والاتصالات غير المتزامنة.
نبدأ أولًا بإنشاء بيئة افتراضية معزولة لتنصيب الحزم الخاصة بالمشروع. يُفضل دائمًا استخدام بيئة افتراضية لفصل إعدادات وتبعيات هذا المشروع عن بقية مشروعات النظام:
python -m venv env
source env/bin/activate # على أنظمة لينكس و macOS
env\Scripts\activate # على نظام ويندوز
بعد ذلك، نثبت حزمة Django وننشئ مشروعًا جديدًا:
pip install django
django-admin startproject chat_project
cd chat_project
حتى يتمكن مشروع Django من التعامل مع الاتصالات اللحظية وWebSockets، نحتاج إلى تثبيت مكتبة Django Channels ومخدم ASGI المعروف باسم Daphne:
pip install channels daphne
تُعد Daphne بمثابة خادم ASGI الأساسي الذي سيتعامل مع الطلبات في تطبيقنا، بينما Django Channels توفر الأدوات اللازمة لإدارة تلك الاتصالات.
بمجرد تثبيت الحزم، يجب تعديل ملف asgi.py
الخاص بالمشروع ليستخدم Channels بدلًا من WSGI التقليدي. افتح ملف chat_project/asgi.py
واستبدل محتواه بالتالي:
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chat_project.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
# WebSocket routing سيتم إضافته لاحقًا
})
في هذه المرحلة، أنشأنا بوابة ASGI الأساسية التي يمكنها استقبال الطلبات عبر بروتوكول HTTP، وسنضيف مسارات WebSocket لاحقًا أثناء إعداد التطبيق.
أخيرًا، علينا إعلام Django بوجود Channels ضمن التطبيقات المثبتة، وتحديد التطبيق الافتراضي للتعامل مع ASGI.
افتح ملف settings.py
وأضف Channels إلى قائمة INSTALLED_APPS
:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels',
]
ثم أضف إعداد ASGI_APPLICATION
ليشير إلى ملف asgi.py
الذي عدلناه:
ASGI_APPLICATION = 'chat_project.asgi.application'
بهذا نكون قد انتهينا من إعداد بيئة العمل الخاصة بمشروع الدردشة اللحظية، وجعلنا مشروع Django جاهزًا لاستقبال اتصالات WebSocket عبر ASGI.
بعد أن أصبح مشروع Django مُجهزًا لدعم الاتصالات غير المتزامنة من خلال ASGI وDjango Channels، يمكننا الآن الانتقال إلى المرحلة التالية، وهي إنشاء التطبيق المسؤول عن إدارة منطق الدردشة داخل المشروع. في Django، يُنظّم المشروع إلى تطبيقات فرعية (Apps)، كل منها يتعامل مع جزء محدد من الوظائف، وفي حالتنا سيكون لدينا تطبيق باسم chat
مخصص للتعامل مع غرف الدردشة والرسائل اللحظية.
من داخل مجلد المشروع الرئيسي chat_project/
، ننفذ الأمر التالي لإنشاء تطبيق جديد:
python manage.py startapp chat
بمجرد تنفيذ هذا الأمر، سينشئ Django مجلدًا باسم chat
يحتوي على الملفات الأساسية الخاصة بالتطبيق، مثل models.py
, views.py
, apps.py
وغيرها.
بعد إنشاء التطبيق، لا بد من إضافته إلى إعدادات المشروع حتى يتعرف عليه Django عند تشغيل الخادم أو تنفيذ أوامر الإدارة.
افتح ملف settings.py
وأضف 'chat',
إلى قائمة INSTALLED_APPS
:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'channels',
'chat',
]
في كثير من حالات تطبيقات الدردشة، قد يكون من الضروري الاحتفاظ بسجل الرسائل في قاعدة البيانات. ورغم أن تطبيقنا في هذا الدليل سيعتمد في التواصل اللحظي على WebSockets دون تخزين الرسائل، إلا أنه يمكن بسهولة إنشاء نموذج بسيط لحفظ الرسائل لاحقًا.
كمثال:
from django.db import models
class Message(models.Model):
room_name = models.CharField(max_length=255)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
بعد إنشاء هذا النموذج، يمكن تنفيذ أوامر makemigrations
و migrate
لاحقًا لتحديث قاعدة البيانات، إن أردنا دعم خاصية الأرشفة.
من المتطلبات الأساسية لتطبيق يعتمد على WebSocket أن يحدد مسارات الاتصالات (Routes) الخاصة به. في Django Channels، يتم ذلك من خلال ملف routing.py
داخل كل تطبيق معني بالتعامل مع WebSocket.
داخل مجلد chat/
، أنشئ ملفًا جديدًا باسم routing.py
، وسنضيف فيه مسارات WebSocket لاحقًا عند إعداد المستهلك (Consumer).
في هذه المرحلة، لا حاجة لإضافة شيء في هذا الملف، لكن من المهم تجهيز هيكله مبكرًا حتى يتكامل بسلاسة مع إعدادات المشروع لاحقًا.
بعد أن أنشأنا تطبيق chat
داخل مشروع Django، وحضّرنا ملف routing.py
الخاص به، تأتي مهمة الربط بين طلبات WebSocket الواردة من العملاء وعمليات المستهلك (Consumer) التي ستتعامل مع هذه الاتصالات. في نظام Django Channels، تتم هذه العملية عبر إعداد ASGI routing الذي يوجه البروتوكولات المختلفة (مثل HTTP و WebSocket) إلى نقاط التعامل المناسبة.
في البداية، نحتاج لتعريف مسارات WebSocket في تطبيق chat
. نفتح ملف chat/routing.py
ونكتب فيه كالتالي:
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
هنا استخدمنا تعبيرًا نظاميًا (regex) لتحديد مسار WebSocket، بحيث يربط الطلبات القادمة إلى المسار /ws/chat/<room_name>/
مع المستهلك ChatConsumer
.
نستخدم as_asgi()
لأن المستهلك عبارة عن ASGI application يُمكن استدعاؤه بهذه الطريقة.
بعد إعداد مسارات WebSocket الخاصة بالتطبيق، يجب ربطها مع إعدادات المشروع العامة عبر ملف routing.py
في مجلد المشروع الرئيسي chat_project/
.
ننشئ ملفًا جديدًا chat_project/routing.py
(إذا لم يكن موجودًا مسبقًا) ونكتب فيه:
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter({
# التعامل مع طلبات HTTP التقليدية عبر Django
"http": get_asgi_application(),
# التعامل مع طلبات WebSocket وتمريرها عبر Middleware للمصادقة
"websocket": AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
هنا نستخدم ProtocolTypeRouter
لتوجيه الطلبات بحسب نوع البروتوكول.
AuthMiddlewareStack
يضيف دعمًا للمصادقة على اتصالات WebSocket بحيث يمكن التحقق من المستخدمين.
URLRouter
يقوم بتوجيه طلبات WebSocket إلى قائمة المسارات التي عرفناها في chat.routing
.
بعد تجهيز ملف routing الرئيسي، نحتاج لتحديث ملف asgi.py
ليستخدم هذا التطبيق الجديد.
نفتح chat_project/asgi.py
ونعدل كالتالي:
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import chat.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chat_project.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
يمكن ملاحظة أننا دمجنا تعريف application
ليشمل HTTP و WebSocket مع دعم مصادقة المستخدم.
في Django Channels، المستهلك (Consumer) هو مكون البرمجيات المسؤول عن التعامل مع الاتصالات غير المتزامنة مثل WebSocket. يشبه إلى حد كبير الـ View في Django التقليدي، لكنه يتعامل مع الأحداث والبروتوكولات بشكل مباشر، مثل فتح اتصال WebSocket، استقبال الرسائل، إرسالها، وإغلاق الاتصال.
ننتقل إلى ملف chat/consumers.py
، ونكتب فيه الكود التالي:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
# استخراج اسم غرفة الدردشة من رابط URL
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = f'chat_{self.room_name}'
# الانضمام إلى مجموعة القناة الخاصة بالغرفة
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
# قبول اتصال WebSocket
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
}
)
# استقبال رسالة من مجموعة القناة وإرسالها إلى WebSocket
async def chat_message(self, event):
message = event['message']
# إرسال الرسالة للعميل (مستخدمي الغرفة)
await self.send(text_data=json.dumps({
'message': message
}))
connect
: عند فتح اتصال WebSocket، يتم استخراج اسم الغرفة، ويتم الانضمام إلى مجموعة القناة التي تمثل غرفة الدردشة. ثم يتم قبول الاتصال.
disconnect
: عند قطع الاتصال، يترك المستخدم مجموعة القناة.
receive
: عند استقبال رسالة من العميل، يتم إرسالها إلى المجموعة التي تمثل الغرفة، بحيث تصل لجميع المستخدمين المتصلين بنفس الغرفة.
chat_message
: تعالج الرسائل التي تصل من المجموعة، وتقوم بإرسالها للعميل الحالي.
لكي تعمل group_add
و group_send
يجب إعداد طبقة القنوات (Channel Layer) التي تستخدم وسيطًا (Backend) مثل Redis لتبادل الرسائل بين العملاء والخادم.
قم بتثبيت Redis على جهازك (أو استخدم خدمة سحابية).
ثبت مكتبة channels_redis
:
pip install channels_redis
ثم في ملف settings.py
أضف الإعداد التالي:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
يمكنك تعديل 'hosts'
حسب إعدادات Redis الخاصة بك.
بعد الانتهاء من إعداد المستهلك (Consumer) الذي يدير اتصالات WebSocket على الخادم، ننتقل إلى جانب العميل (Client) حيث يتم إنشاء واجهة المستخدم التي تتواصل مع الخادم في الوقت الفعلي لإرسال واستقبال الرسائل.
داخل تطبيق chat
، يمكنك إنشاء مجلد باسم templates/chat/
(إذا لم يكن موجودًا)، ثم إنشاء ملف room.html
يحتوي على الكود التالي:
<!DOCTYPE html>
<html>
<head>
<title>غرفة الدردشة</title>
</head>
<body>
<h2>غرفة الدردشة: {{ room_name }}</h2>
<div id="chat-log" style="border:1px solid #ccc; height:300px; overflow-y:scroll; padding:10px;">
</div>
<input id="chat-message-input" type="text" size="100" autofocus placeholder="أدخل رسالتك هنا...">
<button id="chat-message-submit">إرسال</button>
<script>
const roomName = "{{ room_name }}";
const chatLog = document.getElementById('chat-log');
const input = document.getElementById('chat-message-input');
const submitButton = document.getElementById('chat-message-submit');
// إنشاء اتصال WebSocket مع مسار الغرفة
const chatSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/chat/' + roomName + '/'
);
// استقبال الرسائل من الخادم
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
const message = data['message'];
const messageElement = document.createElement('div');
messageElement.textContent = message;
chatLog.appendChild(messageElement);
chatLog.scrollTop = chatLog.scrollHeight; // تمرير التمرير لأسفل تلقائيًا
};
// التعامل مع إغلاق الاتصال
chatSocket.onclose = function(e) {
console.error('اتصال WebSocket مغلق غير متوقع');
};
// إرسال الرسالة عند الضغط على زر الإرسال
submitButton.onclick = function(e) {
const message = input.value;
if (message) {
chatSocket.send(JSON.stringify({
'message': message
}));
input.value = '';
}
};
// إرسال الرسالة عند الضغط على زر الإدخال (Enter)
input.addEventListener('keyup', function(e) {
if (e.key === 'Enter') {
submitButton.click();
}
});
</script>
</body>
</html>
الصفحة تعرض عنوانًا به اسم الغرفة التي يدخلها المستخدم.
يوجد صندوق نصي لإدخال الرسائل وزر إرسال.
يستخدم جافاسكريبت لإنشاء اتصال WebSocket مع الخادم عبر عنوان الغرفة.
كل رسالة تصل من الخادم تُضاف تلقائيًا إلى سجل الدردشة في الصفحة.
يمكن للمستخدم إرسال الرسائل بالضغط على الزر أو مفتاح الإدخال.
في ملف chat/views.py
، نضيف دالة بسيطة لعرض الصفحة مع تمرير اسم الغرفة:
from django.shortcuts import render
def room(request, room_name):
return render(request, 'chat/room.html', {
'room_name': room_name
})
وفي ملف chat/urls.py
(قم بإنشائه إن لم يكن موجودًا):
from django.urls import path
from . import views
urlpatterns = [
path('chat/<str:room_name>/', views.room, name='room'),
]
ثم في ملف chat_project/urls.py
، استورد وأضف مسارات التطبيق:
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('chat.urls')),
]
بهذه الخطوات نكون قد أنشأنا واجهة مستخدم بسيطة تمكن المستخدمين من دخول غرفة دردشة وإرسال واستقبال الرسائل بشكل لحظي.
بما أن Django Channels يعتمد على Redis كوسيط للرسائل، تأكد من تشغيل خادم Redis على جهازك. يمكنك تشغيل Redis باستخدام الأمر التالي في نظام لينكس أو ماك:
redis-server
أو إذا كنت تستخدم ويندوز، فتأكد من تشغيل خدمة Redis من خلال مدير الخدمات أو عبر البرنامج المخصص.
بعد التأكد من تشغيل Redis، شغّل خادم Django باستخدام الأمر:
python manage.py runserver
يعمل هذا الأمر على تشغيل الخادم مع دعم ASGI، وبالتالي يدعم WebSocket.
افتح متصفح الإنترنت وادخل إلى العنوان:
http://127.0.0.1:8000/chat/room1/
حيث room1
هو اسم غرفة الدردشة التي تريد الدخول إليها.
افتح نافذتين أو أكثر في المتصفح أو استخدم أجهزة مختلفة للدخول إلى نفس الغرفة.
ابدأ بإرسال رسائل من نافذة إلى أخرى، ستشاهد أن الرسائل تظهر في الوقت الفعلي لجميع المتصلين بالغرفة.
لقد تعلمنا في هذا الدليل كيفية بناء تطبيق دردشة بسيط باستخدام Django Channels، وذلك عبر الخطوات التالية:
إعداد بيئة العمل وتثبيت الحزم المطلوبة.
تجهيز ملفات التوجيه (Routing) الخاصة بـ WebSocket.
كتابة المستهلك (Consumer) لإدارة الاتصالات والرسائل.
إعداد طبقة القنوات باستخدام Redis.
بناء واجهة مستخدم بسيطة تعتمد على WebSocket.
تشغيل واختبار التطبيق.
يمكن توسيع هذا التطبيق بعدة طرق مثل إضافة المصادقة، حفظ المحادثات في قاعدة البيانات، دعم غرف متعددة مع قوائم مستخدمين، وتحسين تجربة المستخدم.
هذا المشروع يُعد نقطة انطلاق ممتازة لفهم كيفية التعامل مع الاتصالات اللحظية في Django باستخدام Django Channels.
تعلّم كيفية بناء تطبيق دردشة في الوقت الحقيقي باستخدام Django Channels خطوة بخطوة، مع شرح آلية WebSockets، إعداد المستهلكات، وإنشاء واجهة دردشة تفاعلية.
مساحة اعلانية
المكتبات في بايثون هي مجموعات من الوحدات والحزم التي توفر أكواد مكتوبة مسبقًا لتنفيذ مهام مختلفة. تساعد في تبسيط عملية البرمجة من خلال توفير دوال وفئات قابلة لإعادة الاستخدام لوظائف محددة، مثل تحليل البيانات، تعلم الآلة، تطوير الويب، والمزيد.