Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Demo:
## Requirements:

- python 3.10
- django 4.1
- django 5
- channels
- htmx > 1.8.5

Expand All @@ -36,10 +36,11 @@ python -m venv venv
source venv/bin/activate
```

3. Install the required dependencies:
3. Install the required dependencies, we use pip-tools for managing dependencies

```
pip install -r requirements.txt
pip install pip-tools
pip-sync
```

4. Make Migrations:
Expand All @@ -54,3 +55,10 @@ python manage.py migrate
python manage.py runserver
```

6. Create an admin user:

```
python manage.py createsuperuser
```

7. Visit the admin panel at http://localhost:8000/admin
29 changes: 28 additions & 1 deletion accounts/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
from django.contrib import admin

# Register your models here.
from accounts.models import User


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ("username", "email", "is_superuser", "is_staff")
search_fields = (
"username",
"email",
)
list_filter = ("is_superuser", "is_staff")

fieldsets = (
(
None,
{
"fields": (
"username",
"email",
"is_superuser",
"is_staff",
"is_active",
"last_login",
"date_joined",
)
},
),
)
6 changes: 3 additions & 3 deletions accounts/forms.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from accounts.models import User
from django.contrib.auth.forms import UserCreationForm

class UserRegisterForm(UserCreationForm):

class UserRegisterForm(UserCreationForm):
class Meta:
model= User
fields = ['username', 'password1', 'password2']
model = User
fields = ["username", "password1", "password2"]
1 change: 0 additions & 1 deletion accounts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@


class Migration(migrations.Migration):

initial = True

dependencies = [
Expand Down
1 change: 1 addition & 0 deletions accounts/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
pass
4 changes: 2 additions & 2 deletions accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ def register(request):
user = authenticate(username=username, password=raw_password)
login(request, user)
messages.success(
request, f"Your account has been created and you are now logged in as {username}"
request,
f"Your account has been created and you are now logged in as {username}",
)
return redirect("index")
else:
form = UserRegisterForm()
return render(request, "accounts/register.html", {"form": form})

16 changes: 7 additions & 9 deletions backchat/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backchat.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backchat.settings")

from chat import routing


application = ProtocolTypeRouter({
'http': get_asgi_application(),
"websocket":AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
)
})
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)),
}
)
8 changes: 4 additions & 4 deletions backchat/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",

"channels",

"accounts",
"chat",
]
Expand All @@ -38,7 +36,9 @@
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": ["templates",],
"DIRS": [
"templates",
],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
Expand Down Expand Up @@ -94,7 +94,7 @@
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

CHANNEL_LAYERS = {
'default': {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}
Expand Down
4 changes: 2 additions & 2 deletions backchat/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

urlpatterns = [
path("admin/", admin.site.urls),
path("auth/", include('accounts.urls')),
path("", include('chat.urls')),
path("auth/", include("accounts.urls")),
path("", include("chat.urls")),
]
51 changes: 50 additions & 1 deletion chat/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,52 @@
from django.contrib import admin

# Register your models here.
from chat.models import Message
from chat.models import Room


@admin.register(Room)
class RoomAdmin(admin.ModelAdmin):
list_display = (
"name",
"slug",
)
search_fields = (
"name",
"slug",
)
filter_horizontal = ("users",)


@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
readonly_fields = ["created_at"]

list_display = (
"room",
"user",
"message",

)
search_fields = (
"room",
"user",
"message",
)
list_filter = (
"room",
"user",
)

fieldsets = (
(
None,
{
"fields": (
"room",
"user",
"message",
"created_at",
)
},
),
)
50 changes: 31 additions & 19 deletions chat/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ async def connect(self):
self.room_group_name = "chat_%s" % self.room_name
self.user = self.scope["user"]

await self.channel_layer.group_add(
self.room_group_name, self.channel_name
)
await self.channel_layer.group_add(self.room_group_name, self.channel_name)

await self.add_user(self.room_name, self.user)

await self.accept()

messages = await self.get_room_messages(self.room_name)
for message in messages:
message_html = (
f"<div hx-swap-oob='beforeend:#messages'>"
f"<p><b>{message['username']}</b>: {message['text']}</p></div>"
)
await self.send(text_data=json.dumps({"history": message_html}))

async def disconnect(self, close_code):
await self.remove_user(self.room_name, self.user)
await self.channel_layer.group_discard(
self.room_group_name, self.channel_name
)
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)

async def receive(self, text_data):
text_data_json = json.loads(text_data)
Expand All @@ -33,35 +36,44 @@ async def receive(self, text_data):
username = user.username
room = self.room_name

#await self.save_message(room, user, message)
await self.save_message(room, user, message)

await self.channel_layer.group_send(
self.room_group_name,
self.room_group_name,
{
"type": "chat_message",
"message": message,
#"room": room,
"room": room,
"username": username,
}
},
)

async def chat_message(self, event):
message = event["message"]
#room = event["room"]
room = event["room"]
username = event["username"]


message_html = f"<div hx-swap-oob='beforeend:#messages'><p><b>{username}</b>: {message}</p></div>"
message_html = (
f"<div hx-swap-oob='beforeend:#messages'>"
f"<p><b>{username}</b>: {message}</p></div>"
)
await self.send(
text_data=json.dumps(
{
"message": message_html,
#"room": room,
"username": username
}
{"message": message_html, "room": room, "username": username}
)
)

@sync_to_async
def get_room_messages(self, room_slug):
room = Room.objects.get(slug=room_slug)
messages = Message.objects.filter(room=room).order_by("-created_at")[
:50
] # Adjust the number of messages as needed
return [
{"text": message.message, "username": message.user.username}
for message in messages
]

@sync_to_async
def save_message(self, room, user, message):
room = Room.objects.get(slug=room)
Expand Down
1 change: 0 additions & 1 deletion chat/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class Migration(migrations.Migration):

initial = True

dependencies = []
Expand Down
1 change: 0 additions & 1 deletion chat/migrations/0002_room_users_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("chat", "0001_initial"),
Expand Down
3 changes: 2 additions & 1 deletion chat/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db import models
from accounts.models import User


class Room(models.Model):
name = models.CharField(max_length=128)
slug = models.SlugField(unique=True)
Expand All @@ -17,4 +18,4 @@ class Message(models.Model):
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return (self.room.name + " - " + str(self.user.username) + " : " + str(self.message))
return f"{self.room.name} - {str(self.user.username)} : {str(self.message)}"
2 changes: 1 addition & 1 deletion chat/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
from chat import consumers

websocket_urlpatterns = [
path('chat/<str:room_slug>/', consumers.ChatConsumer.as_asgi()),
path("chat/<str:room_slug>/", consumers.ChatConsumer.as_asgi()),
]
9 changes: 5 additions & 4 deletions chat/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@


urlpatterns = [
path("", TemplateView.as_view(template_name="base.html"), name='index'),
path("chat/room/<str:slug>/", views.index, name='chat'),
path("create/", views.room_create, name='room-create'),
path("join/", views.room_join, name='room-join'),
path("", TemplateView.as_view(template_name="base.html"), name="index"),

path("chat/room/<str:slug>/", views.index, name="chat"),
path("create/", views.room_create, name="room-create"),
path("join/", views.room_join, name="room-join"),
]
Loading