Part 1: Basic Setup
In this tutorial we will build a simple chat server. It will have two pages:
An index view that lets you type the name of a chat room to join.
A room view that lets you see messages posted in a particular chat room.
The room view will use a WebSocket to communicate with the Django server and listen for any messages that are posted.
We assume that you are familiar with basic concepts for building a Django site. If not we recommend you complete the Django tutorial first and then come back to this tutorial.
We assume that you have Django installed already and the Channels Tutorial made.
Next, install DCRF into the same environment that was used to setup the Channels Tutorial.
pip install djangochannelsrestframework
- This will be the directory tree at the end of the Channels Tutorial and we will add the following Python files:
serializers.pymodels.pyrouting.py
mysite/
manage.py
mysite/
__init__.py
asgi.py
settings.py
urls.py
wsgi.py
chat/
__init__.py
consumers.py
models.py
serializers.py
routing.py
templates/
chat/
index.html
room.html
tests.py
urls.py
views.py
Creating the Models
We will put the following code in the models.py file, to handle current rooms, messages, and current users.
# chat/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
class Room(models.Model):
name = models.CharField(max_length=255, null=False, blank=False, unique=True)
current_users = models.ManyToManyField(User, related_name="current_rooms", blank=True)
def __str__(self):
return f"Room({self.name} {self.host})"
class Message(models.Model):
room = models.ForeignKey("chat.Room", on_delete=models.CASCADE, related_name="messages")
text = models.TextField(max_length=500)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="messages")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Message({self.user} {self.room})"
Update AUTH Settings
Add the following to mysite/settings.py to properly register the new User Model.
AUTH_USER_MODEL = 'chat.User'
Creating the Serializers
We will put the following code in the serializers.py file, to handle the serialization of the models created.
# chat/serializers.py
from .models import User, Room, Message
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ["password"]
class MessageSerializer(serializers.ModelSerializer):
created_at_formatted = serializers.SerializerMethodField()
user = UserSerializer()
class Meta:
model = Message
exclude = []
depth = 1
def get_created_at_formatted(self, obj:Message):
return obj.created_at.strftime("%d-%m-%Y %H:%M:%S")
class RoomSerializer(serializers.ModelSerializer):
last_message = serializers.SerializerMethodField()
messages = MessageSerializer(many=True, read_only=True)
class Meta:
model = Room
fields = ["pk", "name", "messages", "current_users", "last_message"]
depth = 1
read_only_fields = ["messages", "last_message"]
def get_last_message(self, obj:Room):
return MessageSerializer(obj.messages.order_by('created_at').last()).data
Creating the Consumers
In the consumers.py file, we will create only the room consumer.
# chat/consumers.py
import json
from channels.db import database_sync_to_async
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
from djangochannelsrestframework.observer import model_observer
from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin, action
from .models import Message, Room, User
from .serializers import MessageSerializer, RoomSerializer, UserSerializer
class RoomConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer):
queryset = Room.objects.all()
serializer_class = RoomSerializer
lookup_field = "pk"
Routing the Websocket
# chat/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/room/$', consumers.RoomConsumer.as_asgi()),
]