Django Channel实时推送与聊天的示例代码


Posted in Python onApril 30, 2020

先来看一下最终的效果吧

Django Channel实时推送与聊天的示例代码

开始聊天,输入消息并点击发送消息就可以开始聊天了

Django Channel实时推送与聊天的示例代码

点击 “获取后端数据”开启实时推送

Django Channel实时推送与聊天的示例代码

先来简单了解一下 Django Channel

Channels是一个采用Django并将其功能扩展到HTTP以外的项目,以处理WebSocket,聊天协议,IoT协议等。它基于称为ASGI的Python规范构建。

它以Django的核心为基础,并在其下面分层了一个完全异步的层,以同步模式运行Django本身,但异步处理了连接和套接字,并提供了以两种方式编写的选择,从而实现了这一点。

详情请参考官方文档:https://channels.readthedocs.io/en/latest/introduction.html

再简单说下ASGI是什么东东吧

ASGI 由 Django 团队提出,为了解决在一个网络框架里(如 Django)同时处理 HTTP、HTTP2、WebSocket 协议。为此,Django 团队开发了 Django Channels 插件,为 Django 带来了 ASGI 能力。
在 ASGI 中,将一个网络请求划分成三个处理层面,最前面的一层,interface server(协议处理服务器),负责对请求协议进行解析,并将不同的协议分发到不同的 Channel(频道);频道属于第二层,通常可以是一个队列系统。频道绑定了第三层的 Consumer(消费者)。

详情请参考官方文档:https://channels.readthedocs.io/en/latest/asgi.html

下边来说一下具体的实现步骤

一、安装channel

pip3 install channels 
pip3 install channels_redis

二、新建Django项目

1.新建项目

django-admin startproject mysite

2.新建应用

python3 manage.py startapp chat

3.编辑mysite/settings.py文件

#注册应用
INSTALLED_APPS = [
  ....
  'chat.apps.ChatConfig',
  "channels",
]

# 在文件尾部新增如下配置

#将ASGI_APPLICATION设置设置为指向该路由对象作为您的根应用程序:
ASGI_APPLICATION = 'mysite.routing.application'

#配置Redis
CHANNEL_LAYERS = {
  'default': {
    'BACKEND': 'channels_redis.core.RedisChannelLayer',
    'CONFIG': {
      "hosts": [('10.0.6.29', 6379)],
    },
  },
}

三、详细代码与配置

1. 添加索引视图的模板

chat目录中创建一个templates目录。在您刚刚创建的templates目录中,创建另一个名为的目录chat,并在其中创建一个名为的文件index.html以保存索引视图的模板

将以下代码放入chat/templates/chat/index.html

<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>Chat Rooms</title>
</head>
<body>
  What chat room would you like to enter?<br>
  <input id="room-name-input" type="text" size="100"><br>
  <input id="room-name-submit" type="button" value="Enter">

  <script>
    document.querySelector('#room-name-input').focus();
    document.querySelector('#room-name-input').onkeyup = function(e) {
      if (e.keyCode === 13) { // enter, return
        document.querySelector('#room-name-submit').click();
      }
    };

    document.querySelector('#room-name-submit').onclick = function(e) {
      var roomName = document.querySelector('#room-name-input').value;
      window.location.pathname = '/chat/' + roomName + '/';
    };
  </script>
</body>
</html>

2.创建聊天与消息推送模板

chat/templates/chat/room.html

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.0/jquery.min.js" type="text/javascript"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="external nofollow" >
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
  <meta charset="utf-8"/>
  <title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="150" rows="30" class="text"></textarea><br>
<input id="chat-message-input" type="text" size="150"><br>
<input id="chat-message-submit" type="button" value="发送消息" class="input-sm">
<button id="get_data" class="btn btn-success">获取后端数据</button>
{{ room_name|json_script:"room-name" }}

<script>

  $("#get_data").click(function () {
    $.ajax({
      url: "{% url 'push' %}",
      type: "GET",
      data: {
        "room": "{{ room_name }}",
        "csrfmiddlewaretoken": "{{ csrf_token }}"
      },
    })
  });

  const roomName = JSON.parse(document.getElementById('room-name').textContent);
  const chatSocket = new WebSocket(
    'ws://' + window.location.host
    + '/ws/chat/'
    + roomName + '/'
  );
  let chatSocketa = new WebSocket(
    "ws://" + window.location.host + "/ws/push/" + roomName
  );
  chatSocket.onmessage = function (e) {
    const data = JSON.parse(e.data);
    // data 为收到后端发来的数据
    //console.log(data);
    document.querySelector('#chat-log').value += (data.message + '\n');
  };
  chatSocketa.onmessage = function (e) {
    let data = JSON.parse(e.data);
    //let message = data["message"];
    document.querySelector("#chat-log").value += (data.message + "\n");
  };


  chatSocket.onclose = function (e) {
    console.error('Chat socket closed unexpectedly');
  };
  chatSocketa.onclose = function (e) {
    console.error("Chat socket closed unexpectedly");
  };
  document.querySelector('#chat-message-input').focus();
  document.querySelector('#chat-message-input').onkeyup = function (e) {
    if (e.keyCode === 13) { // enter, return
      document.querySelector('#chat-message-submit').click();
    }
  };

  document.querySelector('#chat-message-submit').onclick = function (e) {
    const messageInputDom = document.querySelector('#chat-message-input');
    const message = messageInputDom.value;
    chatSocket.send(JSON.stringify({
      'message': message
    }));
    messageInputDom.value = '';
  };
</script>
</body>
</html>

3.创建房间的视图

将以下代码放入chat/views.py

# chat/views.py
from django.shortcuts import render
from django.http import JsonResponse
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


def index(request):
  return render(request, "chat/index.html")


def room(request, room_name):
  return render(request, "chat/room.html", {"room_name": room_name})


def pushRedis(request):
  room = request.GET.get("room")
  print(room)

  def push(msg):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
      room,
      {"type": "push.message", "message": msg, "room_name": room}
    )

  push("推送测试", )
  return JsonResponse({"1": 1})

4. 创建项目二级路由

在chat目录下创建一个名为的文件urls.py

# mysite/chat/urls.py
from django.urls import path
from . import views

urlpatterns = [
  path('', views.index, name='index'),
 path('<str:room_name>/', views.room, name='room'),
]

5. 修改根路由

# mysite/urls.py

from django.contrib import admin
from django.urls import path, include
from chat.views import pushRedis

urlpatterns = [
  path('admin/', admin.site.urls),
  path("chat/", include("chat.urls")),
  path("push", pushRedis, name="push"),
]

6.创建一个消费者

文件chat/consumers.py

当Django接受HTTP请求时,它会查询根URLconf来查找视图函数,然后调用该视图函数来处理该请求。同样,当Channels接受WebSocket连接时,它会查询根路由配置以查找使用者,然后在使用者上调用各种功能来处理来自连接的事件。

import time
import json
from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer
from asgiref.sync import async_to_sync
import redis

pool = redis.ConnectionPool(
  host="10.0.6.29",
  port=6379,
  max_connections=10,
  decode_response=True,
)
conn = redis.Redis(connection_pool=pool, decode_responses=True)


class ChatConsumer(AsyncWebsocketConsumer):
  async def connect(self, ):
    self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
    self.room_group_name = "chat_%s" % self.room_name

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

  async def disconnect(self, close_code):
    print("close_code: ", close_code)
    await self.channel_layer.group_discard(
      self.room_group_name,
      self.channel_name
    )

  async def receive(self, text_data=None, bytes_data=None):
    text_data_json = json.loads(text_data)
    message = text_data_json["message"]
    print("receive_message:", message)
    await self.channel_layer.group_send(
      self.room_group_name,
      {
        "type": "chat_message",
        "message": message
      }
    )

  async def chat_message(self, event):
    receive_message = event["message"]
    response_message = "You message is :" + receive_message
    await self.send(text_data=json.dumps({
      "message": response_message
    }))


class PushMessage(WebsocketConsumer):

  def connect(self):
    self.room_group_name = self.scope["url_route"]["kwargs"]["room_name"]
    async_to_sync(self.channel_layer.group_add)(
      self.room_group_name,
      self.channel_name
    )
    self.accept()

  def disconnect(self, code):
    async_to_sync(self.channel_layer.group_discard)(
      self.room_group_name,
      self.channel_name
    )

  def push_message(self, event):
    """
    主动推送
    :param event:
    :return:
    """
    print(event, type(event))
    while True:
      time.sleep(2)
      msg = time.strftime("%Y-%m-%d %H:%M:%S") + "--- room_name: %s" % event["room_name"]
      self.send(text_data=json.dumps(
        {"message": msg}
      ))

7.为项目添加websocket的路由配置

在chat目录下创建一个名为的文件routing.py

# mysite/chat/routing.py

from django.urls import re_path, path
from . import consumers

websocket_urlpatterns = [
  re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer),
  path("ws/push/<room_name>", consumers.PushMessage),
]

8.配置websocket根路由

与setting同级目录新建ws根路由文件 routing.py

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import chat.routing

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

9.最终的文件关系如下图

Django Channel实时推送与聊天的示例代码

10.启动服务

python3 manage.py runserver 10.0.6.2:80

注意看,这和django是不一样的

Django Channel实时推送与聊天的示例代码

还有另一种更稳健的启动方式

和setting同级新增文件 asgi.py

import os
import django
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django.setup()
application = get_default_application()

启动方式为:

daphne -b 10.0.6.2 -p 80 mysite.asgi:application

daphne 在安装channel时已经自动安装好了

Django Channel实时推送与聊天的示例代码

参考:

https://channels.readthedocs.io/en/latest/tutorial/index.html

https://blog.ernest.me/post/asgi-demonstration-realtime-blogging

到此这篇关于Django Channel实时推送与聊天的示例代码的文章就介绍到这了,更多相关Django Channel实时推送与聊天内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
浅析Python编写函数装饰器
Mar 18 Python
Python3.6简单操作Mysql数据库
Sep 12 Python
flask + pymysql操作Mysql数据库的实例
Nov 13 Python
Python语言生成水仙花数代码示例
Dec 18 Python
python画出三角形外接圆和内切圆的方法
Jan 25 Python
python 搜索大文件的实例代码
Jul 08 Python
Python3批量移动指定文件到指定文件夹方法示例
Sep 02 Python
使用python的turtle函数绘制一个滑稽表情
Feb 28 Python
Python xpath表达式如何实现数据处理
Jun 13 Python
python读取excel数据并且画图的实现示例
Feb 08 Python
python实现黄金分割法的示例代码
Apr 28 Python
python 爬取京东指定商品评论并进行情感分析
May 27 Python
Django ORM 查询表中某列字段值的方法
Apr 30 #Python
Python pip install如何修改默认下载路径
Apr 29 #Python
Django 5种类型Session使用方法解析
Apr 29 #Python
PyQt5连接MySQL及QMYSQL driver not loaded错误解决
Apr 29 #Python
Python退出时强制运行一段代码的实现方法
Apr 29 #Python
pyinstaller将含有多个py文件的python程序做成exe
Apr 29 #Python
如何使用python切换hosts文件
Apr 29 #Python
You might like
php中拷贝构造函数、赋值运算符重载
2012/07/25 PHP
php在window iis的莫名问题的测试方法
2013/05/14 PHP
浅析PHP程序防止ddos,dns,集群服务器攻击的解决办法
2013/06/18 PHP
php 批量替换html标签的实例代码
2013/11/26 PHP
PHP反射机制用法实例
2014/08/28 PHP
php使用pdo连接报错Connection failed SQLSTATE的解决方法
2014/12/15 PHP
laravel批量生成假数据的方法
2019/10/09 PHP
laravel5.6 框架操作数据 Eloquent ORM用法示例
2020/01/26 PHP
extjs fckeditor集成代码
2009/05/10 Javascript
jQuery动画效果实现图片无缝连续滚动
2016/01/12 Javascript
js实现动态创建的元素绑定事件
2016/07/19 Javascript
AngularJs bootstrap搭载前台框架——基础页面
2016/09/01 Javascript
JavaScript如何一次性展示几万条数据
2017/03/30 Javascript
详解nodejs模板引擎制作
2017/06/14 NodeJs
Node.js静态服务器的实现方法
2018/02/28 Javascript
js调用设备摄像头的方法
2018/07/19 Javascript
使用jquery的cookie实现登录页记住用户名和密码的方法
2019/03/13 jQuery
vue将后台数据时间戳转换成日期格式
2019/07/31 Javascript
JS实现移动端可折叠导航菜单(现代都市风)
2020/07/07 Javascript
[02:28]DOTA2 2017国际邀请赛小组赛回顾
2017/08/09 DOTA
Python优化技巧之利用ctypes提高执行速度
2016/09/11 Python
Python3连接SQLServer、Oracle、MySql的方法
2018/06/28 Python
Python 取numpy数组的某几行某几列方法
2019/10/24 Python
MATLAB数学建模之画图汇总
2020/07/16 Python
Python实现壁纸下载与轮换
2020/10/19 Python
Html5实现首页动态视频背景的示例代码
2019/09/25 HTML / CSS
Alba Moda瑞士网上商店:独家意大利时尚女装销售
2016/11/28 全球购物
PHP面试题大全
2015/10/16 面试题
大学生标准推荐信范文
2013/11/25 职场文书
个性与发展自我评价
2014/02/11 职场文书
前处理组长岗位职责
2014/03/01 职场文书
中学生关于梦想的演讲稿
2014/08/22 职场文书
派出所班子党的群众路线对照检查材料思想汇报
2014/10/01 职场文书
有关浪费资源的建议书
2015/09/14 职场文书
php png失真的原因及解决办法
2021/11/17 PHP
bootstrapv4轮播图去除两侧阴影及线框的方法
2022/02/15 HTML / CSS