15 KiB
Django Communications - Standalone Setup Guide
О приложении
django-communications - универсальная система чатов для Django с поддержкой:
- 🔌 WebSocket (Django Channels) - real-time messaging
- 💬 Типы чатов: private, group, channel, announcement, global, comments
- 🔗 GenericForeignKey - привязка к любой модели
- 📎 Вложения, реакции, голосования, пересылка сообщений
- 🔔 Уведомления (опционально через django-notifications-hq)
- 🔐 Permissions (django-rules)
- 📱 REST API (Django REST Framework)
Быстрая установка
1. Установка пакета
# Из локальной копии
pip install -e /path/to/communications/
# ИЛИ из PyPI (когда будет опубликован)
pip install django-communications
# С опциональными зависимостями
pip install django-communications[all]
2. Добавить в INSTALLED_APPS
# settings.py
INSTALLED_APPS = [
# Django apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party apps (обязательные)
'rest_framework',
'channels',
'rules',
# Communications
'communications',
# Опциональные (рекомендуется)
'notifications', # для уведомлений
]
3. Настройка
# settings.py
# ===== Django REST Framework =====
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
# ===== Django Channels (WebSocket) =====
ASGI_APPLICATION = 'your_project.asgi.application'
CHANNEL_LAYERS = {
'default': {
# Для разработки - In-Memory
'BACKEND': 'channels.layers.InMemoryChannelLayer',
# Для продакшена - Redis (раскомментируйте)
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
# 'CONFIG': {
# "hosts": [('127.0.0.1', 6379)],
# },
},
}
# ===== django-rules (Permissions) =====
AUTHENTICATION_BACKENDS = (
'rules.permissions.ObjectPermissionBackend',
'django.contrib.auth.backends.ModelBackend',
)
# ===== Communications Settings =====
# URL для профиля автора (ОБЯЗАТЕЛЬНО настроить под ваш проект)
COMMUNICATIONS_AUTHOR_URL_PATTERN = '/users/{id}/' # По умолчанию: '/api/v1/employees/{id}/'
# Функция сжатия изображений (опционально)
COMMUNICATIONS_IMAGE_COMPRESSOR = None # Отключает сжатие
# COMMUNICATIONS_IMAGE_COMPRESSOR = 'myapp.utils.compress_avatar' # Ваша функция
# Resolver для определения участников чата (опционально)
# COMMUNICATIONS_PARTICIPANT_RESOLVER = 'myapp.utils.get_chat_participants'
# Автоматические уведомления (по умолчанию True)
COMMUNICATIONS_AUTO_NOTIFY = True
4. URL routing
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from communications.api.viewsets import ChatViewSet, MessageViewSet, PollViewSet
router = DefaultRouter()
router.register(r'chats', ChatViewSet, basename='chats')
router.register(r'messages', MessageViewSet, basename='messages')
router.register(r'polls', PollViewSet, basename='polls')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/communications/', include(router.urls)),
]
5. ASGI routing (WebSocket)
# asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')
django_asgi_app = get_asgi_application()
from your_project.routing import websocket_urlpatterns
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AuthMiddlewareStack(
URLRouter(websocket_urlpatterns)
),
})
# routing.py (создайте файл)
from django.urls import path
from your_app.consumers import YourConsumer # Ваш consumer с ChatConsumerMixin
websocket_urlpatterns = [
path('ws/', YourConsumer.as_asgi()),
]
6. WebSocket Consumer
# consumers.py
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from communications.consumers import ChatConsumerMixin
class YourConsumer(ChatConsumerMixin, AsyncJsonWebsocketConsumer):
"""
WebSocket consumer с поддержкой чатов.
ChatConsumerMixin предоставляет все методы для работы с чатами.
"""
async def connect(self):
self.user = self.scope["user"]
if not self.user.is_authenticated:
await self.close()
return
self.active_chat_id = None
await self.accept()
# Подключение к персональному каналу
await self.channel_layer.group_add(
f"user_{self.user.id}",
self.channel_name
)
async def disconnect(self, close_code):
if hasattr(self, 'user') and self.user.is_authenticated:
await self.channel_layer.group_discard(
f"user_{self.user.id}",
self.channel_name
)
async def receive_json(self, content):
"""
Обработка сообщений от клиента.
ChatConsumerMixin автоматически обрабатывает chat_* actions.
"""
action = content.get('action')
# Chat actions (обрабатываются автоматически ChatConsumerMixin)
if action in ['open_chat', 'close_chat', 'send_message', 'edit_message',
'delete_message', 'add_reaction', 'remove_reaction',
'typing', 'stop_typing', 'mark_read', 'vote_poll']:
# Вызов соответствующего метода из mixin
handler = getattr(self, f'_handle_{action}', None)
if handler:
await handler(content)
return
# Ваша дополнительная логика
await self.send_json({
'type': 'error',
'error': f'Unknown action: {action}'
})
7. Миграции
python manage.py migrate
8. Создать главный глобальный чат (опционально)
# В Django shell или через signal
from communications.models import Chat
Chat.objects.create(
type='global',
name='Общий чат',
flags={'is_primary': True},
is_main=True # backward compatibility
)
Настройка (детально)
COMMUNICATIONS_AUTHOR_URL_PATTERN
Шаблон URL для профиля автора сообщения. Используется в serialize_message().
# Настройка по умолчанию (проектно-специфичная)
COMMUNICATIONS_AUTHOR_URL_PATTERN = '/api/v1/employees/{id}/'
# Для standalone проекта
COMMUNICATIONS_AUTHOR_URL_PATTERN = '/users/{id}/'
# Отключить URL (пустая строка)
COMMUNICATIONS_AUTHOR_URL_PATTERN = None
Placeholder {id} заменяется на author.id.
COMMUNICATIONS_IMAGE_COMPRESSOR
Функция для сжатия аватаров чатов. Вызывается в pre_save signal.
# Отключить сжатие
COMMUNICATIONS_IMAGE_COMPRESSOR = None
# Использовать свою функцию
COMMUNICATIONS_IMAGE_COMPRESSOR = 'myapp.utils.compress_avatar'
# По умолчанию (backward compatibility)
COMMUNICATIONS_IMAGE_COMPRESSOR = 'common.image_utils.compress_avatar'
Сигнатура функции:
def compress_avatar(image_bytes: bytes) -> bytes:
"""
Принимает байты изображения, возвращает сжатые байты.
"""
from PIL import Image
import io
img = Image.open(io.BytesIO(image_bytes))
# ... ваша логика сжатия ...
return compressed_bytes
COMMUNICATIONS_PARTICIPANT_RESOLVER
Callback для определения участников чата. Используется в Chat.get_participants().
# Не использовать callback (использовать fallback логику)
# COMMUNICATIONS_PARTICIPANT_RESOLVER = None # (по умолчанию)
# Использовать проектно-специфичную логику
COMMUNICATIONS_PARTICIPANT_RESOLVER = 'employees.utils.get_chat_participants'
Сигнатура функции:
from django.db.models import QuerySet
from communications.models import Chat
def get_chat_participants(chat: Chat) -> QuerySet | None:
"""
Возвращает QuerySet участников или None (использовать fallback).
Примеры специфичной логики:
- Для department чата - все сотрудники департамента
- Для project чата - все участники проекта
- И т.д.
"""
if chat.type == 'group' and chat.context_object:
# Пример: context связан с Project
if hasattr(chat.context_object, 'members'):
return chat.context_object.members.all()
return None # Использовать fallback
COMMUNICATIONS_AUTO_NOTIFY
Включает/отключает автоматические уведомления при новых сообщениях.
# Включить (по умолчанию)
COMMUNICATIONS_AUTO_NOTIFY = True
# Отключить (управлять уведомлениями вручную)
COMMUNICATIONS_AUTO_NOTIFY = False
Требует: django-notifications-hq (опционально)
API Endpoints
После настройки доступны следующие endpoints:
Chats
GET /api/communications/chats/- список чатовGET /api/communications/chats/{id}/- детали чатаPOST /api/communications/chats/- создать чатPOST /api/communications/chats/{id}/pin/- закрепитьPOST /api/communications/chats/{id}/notifications/- настроить уведомленияGET /api/communications/chats/{id}/messages/- сообщения (с автоотметкой)
Messages
GET /api/communications/messages/- список сообщенийPOST /api/communications/messages/- отправить сообщениеPATCH /api/communications/messages/{id}/- редактироватьDELETE /api/communications/messages/{id}/- удалитьPOST /api/communications/messages/{id}/react/- добавить реакциюPOST /api/communications/messages/{id}/forward/- переслатьGET /api/communications/messages/search/- поиск
Polls
GET /api/communications/polls/- список голосованийPOST /api/communications/polls/- создать голосованиеPOST /api/communications/polls/{id}/vote/- проголосоватьPOST /api/communications/polls/{id}/close_poll/- закрыть
Документация: См. api/README.md
WebSocket Events
От клиента (action)
{"action": "open_chat", "chat_id": 1}
{"action": "send_message", "chat_id": 1, "content": "Hello"}
{"action": "typing", "chat_id": 1}
{"action": "add_reaction", "message_id": 123, "emoji": "👍"}
От сервера (type)
{"type": "chat_message", "message": {...}}
{"type": "chat_user_typing", "user_id": 5, "chat_id": 1}
{"type": "chat_reaction_added", "message_id": 123, "emoji": "👍"}
Детали: См. consumers.py docstring
Celery Tasks (опционально)
Если используете Celery, добавьте периодическую задачу:
# celery.py
from celery import Celery
from celery.schedules import crontab
app = Celery('your_project')
app.conf.beat_schedule = {
'cleanup-orphaned-attachments': {
'task': 'communications.tasks.cleanup_orphaned_attachments',
'schedule': crontab(hour='*/1'), # Каждый час
},
}
Примеры использования
Создание чата
from communications.models import Chat
from django.contrib.auth import get_user_model
User = get_user_model()
# Личный чат
chat = Chat.objects.create(type='private')
chat.participants.add(user1, user2)
# Групповой чат
chat = Chat.objects.create(
type='group',
name='Команда разработки',
created_by=request.user
)
chat.participants.add(user1, user2, user3)
# Чат привязанный к объекту (GenericFK)
from myapp.models import Project
project = Project.objects.get(id=1)
chat = Chat.objects.create(
type='comments',
context_object=project, # GenericForeignKey
name=f'Комментарии: {project.name}'
)
Отправка сообщения
from communications.models import Message
message = Message.objects.create(
chat=chat,
author=request.user,
content='Привет всем!'
)
Проверка прав доступа
# В view
from rules.contrib.views import PermissionRequiredMixin
class ChatDetailView(PermissionRequiredMixin, DetailView):
model = Chat
permission_required = 'communications.can_view_chat'
Миграция с других систем
Если у вас уже есть проектно-специфичные чаты, используйте:
- GenericForeignKey для привязки к существующим моделям
COMMUNICATIONS_PARTICIPANT_RESOLVERдля логики участников- Миграции данных через management команды
Troubleshooting
Ошибка импорта common.image_utils
Решение: Установите COMMUNICATIONS_IMAGE_COMPRESSOR = None в settings.py
Ошибка импорта employees.models
Решение: Команда verify_chat_migration автоматически пропустит проверку
WebSocket не работает
Проверьте:
- ASGI_APPLICATION настроен
- CHANNEL_LAYERS настроен
- Redis запущен (для продакшена)
- routing.py содержит websocket_urlpatterns
Уведомления не приходят
Проверьте:
django-notifications-hqустановленCOMMUNICATIONS_AUTO_NOTIFY = Truenotificationsв INSTALLED_APPS
Лицензия
MIT License