Django Chat Using Channels - Real Time Chat Tutorial With Authentication

Django Chat Using Channels - Real Time Chat Tutorial With Authentication

/ #Django


Learn how to create a live chat using Django and Channels. Plus how to authenticate users.

Table of contents

Setting up the environment

In this tutorial, I'm going to use a virtual environment for the project we're building. So the first thing we need to do is to instal virtualenv. You can just run this command:

$ pip install virtualenv

Great, the next step now is to create a new environment and activate it.

$ virtualenv djangochat_env
$ source djangochat_env/bin/activate

So the first list here creates a new environment, and the second line activates it. When the environment is activated, we can start installing packages for it.

We can install Django and Channels by running this command:

$ pip install django channels

First, this will install the latest version of Django and a few dependencies Django has. Next, it will install Channels.

What is Channels?
I can quote the creators so it's easy to understand:
"Channels wraps Django’s native asynchronous view support, allowing Django projects to handle not only HTTP, but protocols that require long-running connections too."

Perfect! All of the software we need is installed, so we can continue by creating a Django project and configuring it.

Creating a new Django project is really easy. We just run this command:

$ django-admin startproject djangochat
$ cd djangochat

I want to create a Django app for the core views like the front page, the sign up page and similar.

$ python manage.py startapp core

Even though we have created the app, Django doesn't know that it doesn't exist. So we need to register it in a list of apps. So let's open up the settings.py file.

Here, we can find a list called INSTALLED_APPS. There are a few built in apps from Django already, but we can just append 'core' at the bottom.

'core',

We also need to tell Django about the channels package we installed. Let's add it below the 'core' app.

'channels'

There are two more configurations we need to do before we can start working on the front page.

First, we can tell Django where to look for the ASGI APPLICATION. This can be added where ever you want, but let's put it together with the WSGI APPLICATION.

ASGI_APPLICATION = "djangochat.asgi.application"

Then the last thing we need to do here is to set up the LAYERS configuration for Channels.

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    },
}

Instead of using something like Redis or similar, we're just going to use the servers memory for the messages. So before the messages are stored in the database, they will live in the memory.

Great, the installation and configuration is now finished and we can start building the chat application.

Creating the base and front page

Now that we have a project and an app for the core views, we can continue to the next step which is to create a base template for the project and a simple front page.

Let's begin by creating a new folder called "templates" in the core app folder, and a folder called "core" inside the templates folder. Django will automatically look for a folder called "templates" inside each of the INSTALLED_APPS. The reason I have a folder called "core" inside the templates folder is to make it easier to separate the apps from each other.

Then we can create a new file called "base.html" inside the "core" folder. It should look like this:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Djangochat</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    </head>

    <body>
        <nav class="navbar is-dark is-purple pt-4 pb-4">
            <div class="navbar-brand">
                <a href="/" class="navbar-item is-size-4">Djangochat</a>
            </div>

            <div class="navbar-menu">
                <div class="navbar-end">
                    <a href="/login/" class="navbar-item">Log in</a>

                    <div class="navbar-item">
                        <a href="/signup/" class="button"><strong>Sign up</strong></a>
                    </div>
                </div>
            </div>
        </nav>
    </body>
</html>

This is a simple HTML file where we include Bulma (A CSS framework) and a simple menu with the title and two buttons.

Great, let's create one more file called "frontpage.html" inside the same folder. This file should extend and use the base.html file.

{% extends 'core/base.html' %}

Nice, we now have the two templates. But to render these and show them in the browser, we need a view. So inside the core/views.py file, create a new view for the frontpage.

def frontpage(request):
    return render(request, 'core/frontpage.html')

Then, the last step we need to do before we can test this is to import the view into the urls.py file which is located in the "djangochat" folder. Modify it to look like this:

from django.contrib import admin
from django.urls import path

from core.views import frontpage

urlpatterns = [
    path('', frontpage, name='frontpage'),
    path('admin/', admin.site.urls),
]

What we have done here now is that we have imported the "frontpage" view we just created. Then, we appended the path to this view into the list of urlpatterns.

The first argument is '', this means that when we go to the url for the page, we want it to use this view. Next, we specify which view to use which is frontpage. Then the last argument name is the name of this view. When we set this, we can refer to this page from other places in our project. I'll show you later in this project.

Great, now we can go to the command line and start the development server:

$ python manage.py runserver

Copy the address and open it in your browser. You should now see a simple page.

As you saw in the frontpage.html file, we extended the base.html file. But we didn't add any more data to it.

Do add data to it, we need to add some code blocks to the base.html file. Change the base.html like this:

# Change
<title>Djangochat</title>
# To
<title>{% block title %}{% endblock %}Djangochat</title>

# Below the nav bar, add this code block
{% block content %}
{% endblock %}

We now have the possibility to extend the template better. Let's do some changes to the frontpage.html file as well. Add this below the "{% extends 'core/base.html' %}" code.

{% block title %}Welcome | {% endblock %}

{% block content %}
    <div class="hero">
        <div class="hero-body">
            <h1 class="title has-text-centered has-text-white">Djangochat</h1>
        </div>
    </div>
{% endblock %}

I also want to add a little bit of styling to the page. This is just to make sure that the menu and background is purple.

<style>
    body {
        background: #9c88ff;
    }

    .navbar.is-purple {
        background: #8c7ae6;
    }

    .navbar .button {
        border-color: #9c88ff;
        background: #9c88ff;
        color: #fff;
    }
</style>

And that's it for the base.html file and the frontpage. As you can see, information we write inside the code blocks in the templates that are extended, will be rendered inside there in the other template.

Sign up functionality

For the best security and simpleness, I want to use Django's built in UserCreationForm. Here, we will get a ready form we can use for signing up users and similar.

So the only thing we have to think about is telling Django which fields to show and then create a template for rendering the form.

Inside the core app folder, create a new file called forms.py. It should look like this:

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class SignUpForm(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'password1', 'password2']

First we import a few things from Django. This is the UserCreationForm form class and the User database model.

Next we set up the SignUpForm where we use a meta class to configure it. We need to specify which model to use (Which is User), and then which fields we want the form to use.

We still have a few more steps we need to do. And the next step is to set up the view where we handle the post requests and similar.

So inside the views.py file in the core app folder, we first import the SignUpForm we just created and then we create a new view for the signup page.

from .forms import SignUpForm

def signup(request):
if request.method == 'POST':
    form = SignUpForm(request.POST)

    if form.is_valid():
        user = form.save()

        login(request, user)

        return redirect('frontpage')
else:
    form = SignUpForm()

return render(request, 'core/signup.html', {'form': form})

First we check if the request method is POST. That means that the form has been submitted. And if it has, we can check if the form is valid and create/login the user.

If it's a normal request, we just create an empty instance of the form and render the template.

Next step now is the template. Create a new file called signup.html and add the following contents:

{% extends 'base.html' %}

{% block title %}Sign up | {% endblock %}

{% block content %}
<div class="hero">
    <div class="hero-body">
        <h1 class="title has-text-centered has-text-white">Sign up</h1>
    </div>
</div>

<section class="section has-text-white">
    <div class="columns">
        <div class="column is-4 is-offset-4">
            <form method="post" action=".">
                {% csrf_token %}

                {{ form.as_p }}

                <div class="field">
                    <div class="control">
                        <button class="button">Submit</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</section>
{% endblock %}

Most of this should look familiar. We add a csrf_token to the form to handle security. And then we just show/render the form easily as paragraphs.

The last step before we can test this now is to add the view to the urls.py file.

# First, append the signup view here
from core.views import frontpage, signup

urlpatterns = [
    path('', frontpage, name='frontpage'),
    # Add this path
    path('signup/', signup, name='signup'),
    path('admin/', admin.site.urls),
]

Great. Now we can go to the browser and click the sign up button in the menu. Go ahead and create a new user.

Log out functionality

When we sign up a new user, we are also automatically logged in. And before we implement the log in functionality, I want to implement the log out functionality.

Django has built in functionality for this, and I want to use it. So open up djangochat/urls.py and import this:

from django.contrib.auth import views as auth_views

We import this and rename it to auth_views. This makes it easier to separate from other views we import.

Next, we can append a new path to the urlpatterns:

path('logout/', auth_views.LogoutView.as_view(), name='logout'),

Then the next step will be to show the log out button in the menu instead of log in/sign up when we are authenticated.

{% if request.user.is_authenticated %}
    <div class="navbar-item">
        <a href="/logout/" class="button">Log out</a>
    </div>
{% else %}
    <a href="/login/" class="navbar-item">Log in</a>

    <div class="navbar-item">
        <a href="/signup/" class="button"><strong>Sign up</strong></a>
    </div>
{% endif %}

Before you try this button, I just want to make sure that we are redirected to the correct pages when we log out. Add these three lines to the settings.py file:

LOGOUT_REDIRECT_URL = '/'
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/'

LOGOUT_REDIRECT_URL = The page we are redicted to when we log out.
LOGIN_REDIRECT_URL = The page we are redirected to after login.
LOGIN_URL = The page for the login form.

Nice, now we can test the log out functionality before we start implementing the login functionality.

Log in functionality

Just like we just Django's built in functionality for logging out, we are going to use it for logging in.

It's little bit more complicated now, since loggin in means that we need a login template.

Let's just begin with adding the path to the urlpatterns in the djangochat/urls.py file:

path('login/', auth_views.LoginView.as_view(template_name='core/login.html'), name='login'),

As you can see where, we specify the things we need including the template we are going to create now.

Create a new file called login.html in the core template folder. It should look like this:

{% extends 'base.html' %}

{% block title %}Log in | {% endif %}

{% block content %}
<div class="hero">
    <div class="hero-body">
        <h1 class="title has-text-centered has-text-white">Log in</h1>
    </div>
</div>

<section class="section has-text-white">
    <div class="columns">
        <div class="column is-4 is-offset-4">
            <form method="post" action=".">
                {% csrf_token %}

                <input type="hidden" name="next" value="{{ next }}">

                <div class="field">
                    <label>Username</label>

                    <div class="control">
                        <input type="text" name="username" class="input">
                    </div>
                </div>

                <div class="field">
                    <label>Password</label>

                    <div class="control">
                        <input type="password" name="password" class="input">
                    </div>
                </div>

                {% if form.errors %}
                    <div class="notification is-danger">
                        Your username and password didn't match! Please try again!
                    </div>
                {% endif %}

                <div class="field">
                    <div class="control">
                        <button class="button">Submit</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</section>
{% endblock %}

The template is very easy and most of this should look familiar.

If there are any errors, we just show them using a Bulma notification. There is also a hidden field called "next". This is the URL you will be redirected to after you log in (If you come from a page you didn't have access to).

Create app for rooms

We need to create a new Django app for the rooms. Here we want database models, consumers, views, templates and similar things that will be connected to the rooms.

It's smart to split a Django project into many smaller Django apps because it makes everything more readable and maintainable.

So let's create a new app called "room":

$ python manage.py startapp room

And just like we added 'core' to the list of installed apps, we now need to append 'room' to the list as well. So open up settings.py and add 'room', at the bottom of the list.

'room',

Next step then is to create the database model. A database model is where we describe to Django what information we want to store in the database.

Open up models.py and modify it to look like this:

from django.db import models

class Room(models.Model):
    name = models.CharField(max_length=255)
    slug = models.SlugField()

We only want two fields for the Room model. This is a char field which will be used to store the name of the room and a better name for use in the URL.

Now that we have a database model for the rooms, we need to update the database. So go to the command line and run the following command:

$ python manage.py makemigrations

This will create a few Python files with information about the changes we are doing to the database. The database still hasn't been updated, so we need to run one more command:

$ python manage.py migrate

What this command does is that it runs the Python files that was created when we executed the makemigrations command. So now the database should be updated and we should have a table for our rooms.

Create superuser

Django comes with an admin interface where we easily can add information to the database. But in order to access this admin interface, we need a super user.

To create this superuser, we need to run this command and follow the wizard:

$ python manage.py createsuperuser

If we now start the web server again, and go to http://127.0.0.1:8000/admin/, we can log in with the user we just created.

As you can see, you will now get the possibility to add users. But where is the Rooms model we just created?

Per default, models aren't automatically added to the list here. So we need to open up rooms/admin.py to add it.

from django.contrib import admin

from .models import Room

admin.site.register(Room)

Great, if we go back now and refersh. There will be a new option. Let's go ahead and add a few chat rooms to our application.

Show list of rooms

To show the different chat rooms, I first want to create a new view for this. Open up room/views.py and add the following:

 # Import the Room database model
from .models import Room

# View for the rooms
def rooms(request):
    rooms = Room.objects.all()

    return render(request, 'room/rooms.html', {'rooms': rooms})

Here we just get all of the rooms from the database and add them to the context which will be available in the template.

Then, we can create the template which will list the rooms for us:

{% extends 'core/base.html' %}

{% block title %}Rooms | {% endblock %}

{% block content %}
<div class="hero">
    <div class="hero-body">
        <h1 class="title has-text-centered has-text-white">Rooms</h1>
    </div>
</div>

<section class="section has-text-white">
    <div class="columns is-multiline">
        {% for room in rooms %}
        <div class="column is-4 has-text-centered">
            <div class="box">
                <h2 class="subtitle">{{ room.name }}</h2>

                <a href="#" class="button">Join</a>
            </div>
        </div>
        {% endfor %}
    </div>
</section>
{% endblock %}

Here we're still extending the base.html template. In the columns, we use a for loop to loop through all of the rooms. We still cannot click the Join button, but at least all of the rooms will be listed here.

To keep the main urls.py file as clean as possible, I want to create a separate urls.py file for the room app.

# room/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.rooms, name='rooms'),
]

You might notice that the path is empty here (just like the frontpage). This is because in the main urls.py file, I want to pass through all urls starting with "rooms" to this urls.py file.

Open up djangochat/urls.py. On the same line as we import path, append ", include" at the end like this:

from django.urls import path, include

And then inside the urlpatterns, append this path:

path('rooms/', include('room.urls'))

So if you go to "http://127.0.0.1:8000/rooms/" now, you will match this path and sent to the room/urls.py file where you will match the "empty" path.

Go ahead and visit that url now, and the rooms you created in the previous part should be listed out.

Room detail view

Now it's time to make it possible to click a room. For this, we need a new view and template.

Let's open up room/views.py and add this view at the bottom:

def room(request, pk):
    room = Room.objects.get(pk=pk)

    return render(request, 'room/room.html', {'room': room})

As you can see, this is very similar to the rooms list view. The main difference is that we get an extra parameter in the view.

The extra paramater "pk" stands for primary key and will be an integer which will represent the ID for the room we want to join.

Next, we get the room from the database based on the primary key (pk).

Then, we just need to create a new template where we show the name of the room and a simple form.

{% extends 'core/base.html' %}

{% block title %}{{ room.name }} | {% endblock %}

{% block content %}
<div class="hero">
    <div class="hero-body">
        <h1 class="title has-text-centered has-text-white">{{ room.title }}</h1>
    </div>
</div>

<section class="section has-text-white">
    <div class="columns is-multiline">
        <div class="column is-8 is-offset-2">
            <div class="box messages" id="chat-messages">
                Messages will be displayed here...
            </div>
        </div>

        <div class="column is-8 is-offset-2">
            <div class="box">
                <div class="media">
                    <div class="media-content">
                        <form method="post" action=".">
                            <div class="field">
                                <div class="control">
                                    <input type="text" name="content" id="chat-message-input" class="input" placeholder="Your message...">
                                </div>
                            </div>

                            <div class="field">
                                <div class="control">
                                    <button class="button" id="chat-message-submit">Submit</button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>
{% endblock %}

Next step is to add this view to the list of urls in the room/urls.py file. Add this below the "rooms" path.

path('<int:pk>/', views.room, name='room'),

Here we say that we expect the url to contain an integer (the ID) and give it the name "pk" as you saw in the view.

Last step before we can test this now is to change the rooms.html a little bit. Replace the anchor tag with this:

<a href="{% url 'room' room.id %}">Join</a>

Here we use a built in template function from Django called 'url'. This takes the name of a path (room) and a list of parameters.

So now this will automatically generate links looking like this: "/rooms/1/" and similar.

Creating a consumer

Finally it's time to start implementing channels and making it possible to join a live chat.

The first step we need to do is to create something called a "consumer". The consumer will handle web socket traffic for us.

Let's create a new file called "consumers.py" in the room app folder. It should look like this(I added comments in the code to make it easier to understand):

# First we import json 
import json

from channels.generic.websocket import AsyncWebsocketConsumer # The class we're using
from asgiref.sync import sync_to_async # Implement later

# Create a consumer class
class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
    # Join room based on name in the URL
    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
    )

Nice. Just like we have a file called urls.py for our pages, we want a similar file for the web sockets.

Create a new file called "routing.py

from django.urls import path

from . import consumers

websocket_urlpatterns = [
    path('ws/<str:room_name>/', consumers.ChatConsumer.as_asgi()), # Using asgi
]

There should already be a file called asgi.py in the djangochat folder. This is entry points for traffic just like this. Open up "djangochat/asgi.py" and make it look like this:

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application

import room.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangochat.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            room.routing.websocket_urlpatterns
        )
    )
})

Joining a chat

First, we need to add a new code block to the base.html file so we can pass in javascript. Add this code block before we close the body tag.

{% block scripts %}{% endblock %}

Next step now is to add more functionality to the script inside room.html to connect to the server. This should be added below the code block for the content.

{% block scripts %}
{{ room.slug|json_script:"json-roomname" }}
{{ request.user.username|json_script:"json-username" }}

<script>
    const roomName = JSON.parse(document.getElementById('json-roomname').textContent);
    const userName = JSON.parse(document.getElementById('json-username').textContent);

    const chatSocket = new WebSocket(
        'ws://'
        + window.location.host
        + '/ws/'
        + roomName
        + '/'
    );

    chatSocket.onmessage = function(e) {
        console.log('onMessage');
    };

    chatSocket.onclose = function(e) {
        console.error('The socket closed unexpectedly');
    };
</script>
{% endblock %}

Sending messages

We can also add the javascript for sending and receiving messages.

chatSocket.onmessage = function(e) {
    const data = JSON.parse(e.data);

    if (data.message) {
        document.querySelector('#chat-messages').innerHTML += ('<b>' + data.username + '</b>: ' + data.message + '<br>');
    } else {
        alert('The message was empty!')
    }
};

document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function(e) {
    if (e.keyCode === 13) {
        document.querySelector('#chat-message-submit').click();
    }
};

document.querySelector('#chat-message-submit').onclick = function(e) {
    e.preventDefault();

    const messageInputDom = document.querySelector('#chat-message-input');
    const message = messageInputDom.value;

    chatSocket.send(JSON.stringify({
        'message': message,
        'username': userName,
        'room': roomName
    }));

    messageInputDom.value = '';

    return false;
};

Now everything in the frontend should be ready.

But there's still some thing that needs to be done in the backend.

# Receive message from WebSocket
async def receive(self, text_data):
    data = json.loads(text_data)
    message = data['message']
    username = data['username']
    room = data['room']

    # Send message to room group
    await self.channel_layer.group_send(
        self.room_group_name,
        {
            'type': 'chat_message',
            'message': message,
            'username': username
        }
    )

# Receive message from room group
async def chat_message(self, event):
    message = event['message']
    username = event['username']

    # Send message to WebSocket
    await self.send(text_data=json.dumps({
        'message': message,
        'username': username
    }))

So if we test now in two windows, we should be able to talk to eachother.

The problem now is that if we refresh, the messages are gone.

Or if you open one more window, the messages is also gone.

To fix this, we need to store the messages in a database.

Storing messages

Open up "room/models.py" and add this new model.

from django.contrib.auth.models import User

class Message(models.Model):
    room = models.ForeignKey(Room, related_name='messages', on_delete=models.CASCADE)
    user = models.ForeignKey(User, related_name='messages', on_delete=models.CASCADE)
    content = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('date_added',)

We also need to get the messages in the view.

# Import the messages model
from .models import Message

# Get the messages from the database
messages = Message.objects.filter(room=room)[0:25]

# Add the messages to the context
'messages': messages

So the whole "room" view should look like this now:

def room(request, pk):
    room = Room.objects.get(pk=pk)
    messages = Message.objects.filter(room=room)[0:25]

    return render(request, 'room/room.html', {'room': room, 'messages': messages})

And we need to add them to the template:

{% for m in messages %}<b>{{ m.user.username }}</b>: {{ m.content }}<br>{% endfor %}

So now we get the messages there, but we also need to store them when their sent. Open up the consumers.py file and make the following changes:

# Import the user model
from django.contrib.auth.models import User

# Import the Message model
from .models import Room, Message

# Add a new method to the class
@sync_to_async
def save_message(self, username, room, message):
    user = User.objects.get(username=username)
    room = Room.objects.get(slug=room)

    Message.objects.create(user=user, room=room, content=message)
# Place this line:
await self.save_message(username, room, message)

# Above this line
await self.channel_layer.group_send(

Scrolling to the bottom

A little issue we have now is that when there are more than a few messages, we will only see the messages at the top of the list. So we want to set a maximum height, make the box scrollable and also send us automatically to the last message.

First, we can add a few lines of CSS to the base.html file:

.messages {
    height: 400px;
    overflow-y: auto;
}

Next, we are going to add JavaScript for the automatic scrolling. This can be done in the rooms.html file.

/**
* A function for finding the messages element, and scroll to the bottom of it.
*/
function scrollToBottom() {
    let objDiv = document.getElementById("chat-messages");
    objDiv.scrollTop = objDiv.scrollHeight;
}

// Add this below the function to trigger the scroll on load.
scrollToBottom();

And then add this line at the bottom of this function (chatSocket.onmessage = function(e)) to trigger the scroll after a message is sent:

scrollToBottom();

Great. So whenever we refresh the window or when there is a new message appearing, we will be sent to the bottom.

Summary

That was it for this tutorial. You should now have a working chat application you can use. It might not be ready for production, but with a few tweaks here and there, it most certainly can be used to anything. The most obvious thing to change is to implement PostgreSQL instead of Sqlite.

If you want to learn even more about this subject, please check out my website premium courses where you can learn to build a Slack clone using the same technology.

Video

Comments

ionecum | May 27, 22 05:31

You have plenty of errors in your tutorial. First of all, your methods in consumers.py are not properly indented and this code will produce an error if you run it.

Second in asgi.py you are importing chat.routing but there is not a module named chat. Remember that you named your app "room" not "chat" then you should import room.routing instead.

Inside URLRouter is the same history. The correct line is chat.routing.websocket_urlpatterns

In room.html you don't put {% csrf_token %} below form, it will not work.

In room/models.py you don't import the User class.

Also you don't mention filenames where you should add methods and this may be confusing for newbies. And finally, you never mention that you should migrate after each change in models.

Despite all this the application is still buggy because the messages are still not displayed. I will not continue to debug your code for nothing. This tutorial is a complete mess it shows a complete lack to attention to detail and it clearly show you never run and tested the code before publishing it.

You must definitively fix all these problems, otherwise the tutorial will worth nothing.

As a final note: by the way you should also improve it a bit visually. The 90s are long gone.


Stein Ove Helset | Jun 02, 22 07:31

Hey!
Thank you so much for the detailed comment. I have fixed everything you've pointed out now.
Everything except the csrf_token in room.html, that shouldn't be there? The submission is handled by web socket and isn't a form/post request.

I know the design isn't the best, but that's not the point for this tutorial anyways.
Btw, check out the video instead:
https://www.youtube.com/watch?v=OyUrMENgZRE

Here I use tailwind and the design is also a bit better :-)

ionecum | Jun 18, 22 04:13

Still errors.

1) You forget to import

from django.contrib.auth import authenticate, login

and
from django.shortcuts import redirect

in core/views.py

2) You must mention that this line and all that follows
{% if request.user.is_authenticated %}

must be in base.html

3) You still not mention that before running
python manage.py runserver

for the first time, you should first run
python manage.py migrate

This is important information for newbies.

4) Change this line
Open up models.py and modify it to look like this:

with
Open up room/models.py and modify it to look like this:

It's the second time I make the same mistake putting it in Core instead of Room.

5) You still don't indent code here, IT'S REQUIRED BY PYTHON
async def connect(self):
# Join room based on name in the URL
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name

6) Create a new file called "routing.py
WHERE????? Please also close your quotes

7) "We can also add the javascript for sending and receiving messages."
WHERE???????????????????????????????????

8) But there's still some thing that needs to be done in the backend.

Again, WHERE?????

Finally, your code doesn't work for the second time. There is no data for rooms, the database is empty, you should have provided some dummy data as an example and

http://127.0.0.1:8000/rooms/1

only produce a template error. "Room matching query does not exist."

TRY AGAIN

AND PLEASE ALWAYS TEST YOUR CODE BEFORE POSTING TUTORIALS THAT DO NOTHING

Ionecum | Jun 18, 22 04:55

I also insist on the following: if you don't put {% csrf_token %} in room.html, you will get CSRF verification failed. Request aborted. If I put it, the JavaScript will not work because the messages are never displayed. I will get Uncaught TypeError: document.querySelector(...) is null. Probably because the JavaScript was put in a wrong place (failure to specify where put things is very serious). I put all the javascript in the end of room.html, but messages are not displayed. You should always test test and test and you should have a working example of the tutorial on github.


Stein Ove Helset | Jun 22, 22 07:04

Hello again, thanks for this and the previous comment. And thanks for taking the time to correct me :-)

I will fix the mistakes asap.
I have a working repository here:
https://github.com/SteinOveHelset/djangochat

Stein Ove Helset | Jul 16, 22 07:10

I have pushed part 4 now, and fixed a few small things as well!

Aine Jimson | Jul 03, 22 08:55

still your code doesn't work boss, your github repo is incomplete, no message storage functionality which is actually the most serious problem with this post.


Stein Ove Helset | Jul 13, 22 07:13

Ah, yes I notice that I haven't pushed part 4 to the github repo. But the code for storing messages in both in the video and in the blog post, you hopefully you're able to assemble it :-)

Stein Ove Helset | Jul 16, 22 07:10

I have pushed part 4 now, and fixed a few small things as well!

yo | Sep 15, 22 11:10

When i send a text, it dosent display
and i have to go back twice.


Stein Ove Helset | Sep 16, 22 05:47

Hey, that sounds like something wrong with the javascript. Are there any errors in the browsers console?

siji | Sep 15, 22 11:44

When i send a text, if i go back and enter the group again, the texts disappear


Stein Ove Helset | Sep 16, 22 05:47

Hey, how far in the tutorial have you come?

Luke | Dec 29, 22 11:22

I'm really disappointed in this tutorial. It seemed alright at first, but the further I got into it the worse it became. half the time you don't tell us which file to put the code in. EVERY TIME. Not half the time. Then I've got to paste it into every possible file, and guess if the errors are because I put it into the wrong file or because your code doesn't work. Add WAY more documentation or just take the tutorial down. It would've been better just to not put it up in the first place its so bad. Wasted my time. Now I've got to start over completely because the entire project is a disorganized mess of half-functional code.


Stein Ove Helset | Dec 30, 22 08:23

Hey, sorry to hear that you didn't like it :/
The name of the file is always visible in top of my editor, so it should hopefully make it easier for you if you try to locate that in the video :-)

I hope that helps :-)

Thank | Nov 07, 23 10:19

Nice job. It was really nice and helpful

Add comment

Info

Please log in to comment!

Newsletter

Subscribe to my weekly newsletter. One time per week I will send you a short summary of the tutorials I have posted in the past week.