windows7
python3.7
django3
websockets
(pip install websockets)
django-admin.py startapp untitled
manage.py startapp chat
# untitled/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'chat',
]
# chat/models.py
from django.db import models
class Room(models.Model):
name = models.CharField(max_length=20)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Message(models.Model):
name = models.CharField(max_length=20)
text = models.TextField()
date = models.DateTimeField(auto_now_add=True)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
def __str__(self):
return f'[name: {self.name}; date: {self.date}]: {self.text}'
python manage.py makemigrations chat
python manage.py migrate
你当然可以建立一个Room:
E:\DjangoProgram\untitled>python manage.py shell
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from chat.models import *
>>> a = Room.objects.create(name='A chat room')
chat下新建文件urls.py和forms.py,再在chat新建文件夹templates
并在templates新建room.html, create.html
# chat/urls.py
from django.urls import path
from . import views
app_name = 'chat'
urlpatterns = [
# 创建房间的路由
path('new_room', views.create_room, name='create'),
# 聊天室
path('/' , views.enter_room, name='room'),
]
# chat/forms.py
from django import forms
class RoomForm(forms.Form):
name = forms.CharField(max_length=20)
# chat/views.py
from django.shortcuts import render, get_object_or_404, reverse
from django.http import HttpResponseRedirect
# Create your views here.
from .models import *
from .forms import *
def enter_room(request, pk):
room = get_object_or_404(Room, pk=pk)
context = {
'room': room.name,
'all_messages': room.message_set.order_by('date'),
}
return render(request, 'room.html', context)
def create_room(request):
form = RoomForm(request.POST or None)
if form.is_valid():
name = request.POST['name']
r = Room.objects.create(name=name)
return HttpResponseRedirect(reverse('chat:room', args=(r.pk, )))
context = {
'form': form
}
return render(request, 'create.html', context)
<html lang="zh">
<head><title>{
{ room }}title>head>
<body>
<div id="log">
{% for all_message in all_messages %}
<p>[name: {
{ all_message.name }} ; date: {
{ all_message.date|date }} {
{ all_message.date|time }}] {
{ all_message.text }}p>
{% endfor %}
div>
<label for="inputname">name:label><input type="text" id="inputname">
<br/>
<label for="input">message:label><textarea id="input" style="width: 100%;height: 100px">textarea>
<br/>
<input type="button" id="submit" value="send">
body>
<script>
const log = document.getElementById('log');
// log用于记录信息
const name = document.getElementById('inputname');
// name字段
const input = document.getElementById('input');
// text字段
const but = document.getElementById('submit');
// 发送按钮
// 建立webosckets对象
const ws = new WebSocket('ws://'+window.location.hostname+':8181'+window.location.pathname);
// 注意,websockets的服务端口与django的设置的不同!!!
// 下面是客户端的websockets(等下我会展示服务端的)
ws.onopen = function () {
alert('connected....');
};
ws.onclose = function () {
alert('closed');
};
ws.onmessage = function (ev) {
const data = JSON.parse(ev.data);
// 当服务端有数据发送...
const name = data['name'];
const message = data['message'];
let dt = Date();
dt = dt.toLocaleString();
const p = document.createElement('p');
p.innerText = '[name: '+name+' ; date: '+dt+'] '+message;
log.appendChild(p);
};
function send() {
const n = name.value;
const m = input.value;
// 向服务端发送数据,用json打包
ws.send(JSON.stringify({
'name': n, 'message': m}))
}
but.onclick = function () {
send();
// 按钮按下执行
input.value = '';
// 清空
}
script>
html>
<html lang="zh">
<head><title>Add roomtitle>head>
<body>
<form method="post" action="{% url 'chat:create' %}">
{% csrf_token %}
{
{ form.as_p }}
<input type="submit" value="submit">
form>
body>
html>
最后:
# untitled/urls.py
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('chat/', include('chat.urls')),
]
不同于网上大部分用dwebsockets, channels
我选择用websockets写服务端
最激动人心也是最容易出问题的部分
在根目录下新建server.py
# server.py
import asyncio
import websockets
import json
import sys
import os
import django
sys.path.append(os.getcwd())
添加路径
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled.settings")
# 这个很重要不然等下在协程中访问数据库就会报错
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
django.setup()
from chat.models import *
# 记录登录用户
websocket_users = set()
async def recv_user_msg(websocket, path):
print(path)
# 解析路径,获取房间pk值
# 注意用/区分路径后最后一个是''(读者可以试试)
# 所以要取倒数第二个
pk = path.split('/')[-2]
room = Room.objects.get(pk=int(pk))
while True:
recv_text = await websocket.recv()
# 解析数据
data = json.loads(recv_text)
# 写入数据库
room.message_set.create(name=data['name'], text=data['message'])
for i in websocket_users:
# 如果有用户与他在同一路将,那么发送信息(联系下面的看)
if i[1] == path:
await i[0].send(recv_text)
# 服务器端主逻辑
async def run(websocket, path):
print(f'you are at path:{path}')
while True:
try:
websocket_users.add((websocket, path))
# 添加元组,第二个记录用户端的路径
await recv_user_msg(websocket, path)
except websockets.ConnectionClosed:
try:
websocket_users.remove((websocket, path))
# 移除该用户的记录
except KeyError:
pass
break
except websockets.InvalidState:
break
except Exception as e:
print(f'Exception :{e}')
def main():
print("127.0.0.1:8181 websocket...")
asyncio.get_event_loop().run_until_complete(websockets.serve(run, "127.0.0.1", 8181))
asyncio.get_event_loop().run_forever()
if __name__ == '__main__':
main()
这段代码我借鉴了网上的,根据需求改了一些
一:python manage.py runserver
二:python server.py
记住,2个程序必须都有启动!!!
最后打开http://127.0.0.1:8000/chat/1
(前提你在前面建立了一个空房间。)
效果截图:
项目结构附图:
这个项目最大的难度就是搞websockets服务端
网上找了许多都没有用
希望对大家有帮助。
如果有问题大家可以指出来,谢谢(我也是个不是老司机。。)