Hello guys. In my Firefox browser, I can’t join a chat room for the server when I click on the ‘Join’ button.
I’m pretty sure I set up everything correctly in all of the files inside my arborchat app, and I don’t see any errors in the browser console, which I’ll share a screenshot.
Here are the files
serializers.py
from rest_framework import serializers
from arborfindr.models import User
from arborchat.models import Room, Messenger, Arborist, ArboristProfile, Company, CompanyProfile
from django.contrib.auth import authenticate
class RegisterChatSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {
'password': {'write_only': True}
}
def validate(self, valid):
if valid['password'] != valid['password']:
raise serializers.ValidationError({"password": "Passwords do not match."})
return valid
def create(self, validated_data):
user = User.objects.create_user(
email=validated_data['email'],
username=validated_data['username'],
password=validated_data['password']
)
return user
class LoginChatSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {
'password': {'write_only': True}
}
email = serializers.CharField(max_length=255)
password = serializers.CharField(
label =("Password"),
style={'input_type': 'password'},
trim_whitespace=False,
max_length=128,
write_only=True
)
def validate(self, data):
email = data.get('email')
password = data.get('password')
if email and password:
user = authenticate(request=self.context.get('request'),
email=email, password=password)
if not user:
msg = ('Invalid email/password')
raise serializers.ValidationError(msg, code = 'Authorization')
else:
msg = ('Must include valid "email" and "password".')
raise serializers.ValidationError(msg, code = 'Authorization')
data['user'] = user
return data
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = ['name', 'online']
class MessengerSerializer(serializers.ModelSerializer):
class Meta:
model = Messenger
fields = ['content', 'timestamp']
class ArboristSeriallizer(serializers.ModelSerializer):
class Meta:
model = Arborist
fields = ['text']
class ArboristProfileSerializer(serializers.ModelSerializer):
class Meta:
model = ArboristProfile
fields = ['arborist_name', 'arborist_location', 'services_arborist', 'description',
'size', 'arborist_logo', 'pricing']
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ['text']
class CompanyProfileSerializer(serializers.ModelSerializer):
class Meta:
model = CompanyProfile
fields = ['company_name', 'company_location', 'services_company', 'company_description',
'company_size', 'company_logo', 'website_link']
urls.py (app)
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from django.conf import settings
from django.contrib.auth import views as auth_views
from django.urls import path
from . import views
from .views import ChatLoginView, ChatRegisterView, ArboristProfileView, CompanyProfileView
urlpatterns = [
path('', views.arborchat_home, name='arborchat-home'),
path('arborist/register/', ChatRegisterView.as_view(), name='arborist-register'), # Register API endpoint url
path('arborist/login/', ChatLoginView.as_view(), name ='arborist-login'), # Login API endpoint url
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('arborist/profile/', ArboristProfileView.as_view(), name='arborist-profile'),
path('company/profile/', CompanyProfileView.as_view(), name='company-profile'),
path('messenger/', views.messenger_room, name='messenger'),
path('messenger/<str:room_name>/', views.room_view, name='messenger-room'),
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
consumers.py
import json
from channels.consumer import AsyncConsumer
from channels.generic.websocket import AsyncJsonWebsocketConsumer
import base64
# Channel consumer for both chat room group
class ChatConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(self.room_group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name, {'type': 'chat.message', 'message': message}
)
# Receive message from room group
async def chat_message(self, event):
message = event['message']
# send message to websocket
await self.send(text_data=json.dumps({'message': message}))
# Channel Consumer that consumed events for uploading/receiving image files in the message room
class UploadImageConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
await self.accept()
async def disconnect(self, close_code):
async def receive(self, bytes_data=None, text_data=None):
if bytes_data:
# Handles image file chunks (binary data)
with open('image.jpg', 'rb') as f:
fcontent = f.read()
await self.send(base64.b64encode(fcontent).decode('utf-8'))
elif text_data:
# Handles metadata (text data)
await self.send(text_data=json.dumps({'message': text_data}))
async def send_image(self, event):
image_data = event['image_text']
await self.send(bytes_data=image_data)
```
routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r’ws/arborchat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi())
]
views
from django.shortcuts import render
from arborchat.models import Room, Arborist
from arborfindr.models import User
from rest_framework.views import APIView
from rest_framework.decorators import authentication_classes, permission_classes
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, DjangoModelPermissionsOrAnonReadOnly, AllowAny
from django.contrib.auth import login
from .serializers import RoomSerializer, RegisterChatSerializer, LoginChatSerializer, ArboristProfileSerializer, CompanyProfileSerializer
from rest_framework.authtoken.models import Token
from rest_framework import status, generics, permissions
from rest_framework_simplejwt.tokens import RefreshToken
from drf_haystack.viewsets import HaystackViewSet
from haystack.query import SearchQuerySet
def arborchat_home(request):
return render(request, ‘arborchat/index.html’, {
‘channels’: Room.objects.values_list(‘room_name’, flat=True)
})
def index(request):
return render(request, ‘arborchat/index.html’, {
‘rooms’: Room.objects.all(),
})
def messenger_room(request, room_name):
room = Room.objects.first()
return render(request, ‘messenger.html’, {‘room’: room })
@authentication_classes([JWTAuthentication])
def room_view(request, room_name):
room, created = Room.objects.get_or_create(name=room_name)
return render(request, ‘messenger.html’, {‘room’: room})
API endpoint for Arborist login
@authentication_classes([JWTAuthentication])
class ChatLoginView(APIView):
authentication_classes = (JWTAuthentication,) # Keep as tuple if you prefer
def post(self, request, *args, **kwargs):
serializer = LoginChatSerializer(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user) # Corrected: Pass the request object
token = Token.objects.create(user=user) # Auth Token
if user is not None: # This block is now reachable
# Generate token
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
return Response({
'message': 'Successful login',
'access_token': access_token,
'refresh_token': str(refresh),
"Token": token.key # Added auth token
}, status=status.HTTP_200_OK)
return Response({'error': 'Invalid email/password'}, status=status.HTTP_401_UNAUTHORIZED) # Now reachable
@authentication_classes([JWTAuthentication])
class ChatRegisterView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterChatSerializer
permission_classes = [permissions.AllowAny]
def post(self, request):
serializer = RegisterChatSerializer(data=request.data, context={'request': request})
if serializer.is_valid():
serializer.save()
return Response({
'message': 'successfully registered', }, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ArboristProfileView(APIView):
permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]
serializer_class = ArboristProfileSerializer
def get(self, request):
user = request.user
serializer = self.serializer_class(user)
return Response(serializer.data)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class CompanyProfileView(APIView):
permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]
serializer_class = CompanyProfileSerializer
def get(self, request):
user = request.user
serializer = self.serializer_class(user)
return Response(serializer.data)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class SearchBarView(APIView):
model = Arborist
permission_classes = [IsAuthenticated]
serializer_classes = ArboristProfileSerializer
success_url = '/'
context_object_name = 'all_search_results'
def get(self):
query = self.request.GET.get('search')
results = SearchQuerySet().filter(content=query)
serialize results based on models
serialized_results = []
for result in results:
if result.model == 'arborist':
serializer = ArboristProfileSerializer(result.object)
elif result.model == 'company':
serializer = CompanyProfileSerializer(result.object)
else:
continue
serialized_results.append(serializer.data)
return Response(serialized_results)
index.html
{% load static %}
ArborHub messenger #arboristRoomSelect { height: 300px; }</body>
```
messenger.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>ArborHub messenger</title>
<link rel="icon" href="{% static 'favicon.ico' %}">
<link rel="stylesheet" href="http://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<script src="http://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
<style>
#messageLog {
height: 300px;
background-color: #FFFFFF;
resize: none;
}
#onlineArboristSelector {
height: 300px;
}
</style>
</head>
<body>
<textarea id='chat-text' cols='80' rows='30'></textarea>
<input id='input' type='text' size='80'><br>
<input id='submit' type='button' value='Send'>
<div id='hello-world'></div>
{{ room_name|json_script:'room-name'}}
<script>
document.querySelector('#submit').onclick = function (e) {
const messageInputDom = document.querySelector('#input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message,
}));
messageInputDom.value = '';
};
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const chatSocket = new WebSocket(
'ws://' +
proxyLoc(location).host +
'/ws/arborchat/' +
roomName +
'/'
);
chatSocket.onmessage = function (e) {
const data = JSON.parse(e.data);
console.log(data)
document.querySelector('#hello-world').innerText = data.message
}
</script>
<div class="container mt-3 p-5">
<h2>ArborHub messenger</h2>
<div class="row">
<div class="col-12 col-md-8">
<div class="mb-2">
<label for="messageLog">Room: #{{ room.name}}</label>
<textarea class="form-control" id="messageLog" readOnly></textarea>
</div>
<div class="input-group">
<input type="text" class="form-control" id="channelMessage" placeholder="Message here">
<div class="input-group-append">
<button class="btn btn-success" id="sendMessage" type="button">Send</button>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<label for="onlineArboristSelector">Online arborists</label>
<select multiple class="form-control" id="onlineArboristSelector">
</select>
</div>
</div>
</div>
</body>
</html>
Finally settings
ASGI_APPLICATION = "MyProject.asgi.application"
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
Let me know if you need additional files