当前位置:知识百科 > 正文

django_◆_channels_◆_layim_实现用户一对一,一对多,群组聊天实时通讯

更新时间:2025-01-03 02:23 阅读量:60385

Django Channels介绍

首先要理解Django现有的请求响应策略是这样的:浏览器发出请求,Django服务器接受请求后通过路由匹配该请求到某个视图,视图将会返回一个响应并由服务器发送回浏览器.类似的请求响应在Flask实现也是如此.对于一般性的网页浏览(比如新闻阅读),这样的响应机制是没有问题的,但对于需要一个保持不断会话的请求来说,这是行不通的,因为Django的声明周期只能存在一个请求中,它不能让服务器在没有请求的情况下不断地发送数据岛浏览器客服端.这样的场景目前正在不断地涌现,例如在线聊天室,会话机器人,以及最近很流行的微服务应用.Channels改变了Django的工作方式,让它实现了一种包括通道、消费者和worker的worker监听的模式,所有消费者都会分配有单独的通道,worker监听通道的消息,确保消息到来时能进行处理.为了确保上述机制运行,Channels需要有三个工作层:

接口服务器,Django和用户(浏览器)之间通信的桥梁,包括一个实现WSGI协议的适配器和一个独立的websocket服务器.

通道后端, 在接口服务器和worker之间传递消息,由插拔式的python代码和存储组成,存储可以是内存、数据库或者redis,推荐使用redis,兼具其余两者的优点.

worker,监听所有channel,当有新消息到来时候唤醒功能函数.

Channels可以让Django的框架变得更为可靠和可拓展,整个通信的服务器数可以按需拓展,至少保证一台协议服务器和一台工作服务器即可.使用Channels后,你不再需要组织code去为异步调用,Channls已经将一切都已经帮你准备好.

实验教程

前端框架:?https://www.layui.com/layim/

演示实例:

用户名:user001

密码:p@ssw0rdwcx

用户名:kefu001

服务端使用IE浏览器打开:https://www.szyfd.xyz/itkf/app/index/

前端使用:?https://www.layui.com/doc/modules/layim.html

运行效果图:

django_◆_channels_◆_layim_实现用户一对一,一对多,群组聊天实时通讯

项目目录:

django_◆_channels_◆_layim_实现用户一对一,一对多,群组聊天实时通讯

1.pycharm 新建django 项目

django_◆_channels_◆_layim_实现用户一对一,一对多,群组聊天实时通讯

2.安装?pip install -r requirements.txt

aioredis==1.2.0
asgiref==3.2.7
asn1crypto==0.24.0
async-timeout==3.0.1
attrs==19.1.0
autobahn==19.9.2
Automat==0.7.0
backports.csv==1.0.7
certifi==2019.6.16
cffi==1.12.3
channels==2.2.0
channels-redis==2.3.3
chardet==3.0.4
constantly==15.1.0
cryptography==2.7
daphne==2.3.0
defusedxml==0.6.0
diff-match-patch==20181111
Django==2.1.11
django-import-export==1.2.0
django-redis==4.10.0
django-simpleui==4.0
django-utils==0.0.2
et-xmlfile==1.0.1
hiredis==1.0.0
hyperlink==19.0.0
idna==2.8
incremental==17.5.0
jdcal==1.4.1
lark-parser==0.7.4
msgpack==0.6.1
numpy==1.17.1
odfpy==1.4.0
openpyxl==2.6.3
optionaldict==0.1.1
Pillow==7.1.2
pycparser==2.19
PyHamcrest==1.9.0
PyMySQL==0.9.3
python-dateutil==2.8.0
pytz==2019.2
PyYAML==5.1.2
redis==3.3.8
requests==2.22.0
required==0.4.0
six==1.12.0
sqlparse==0.3.0
tablib==0.13.0
Twisted==19.7.0
txaio==18.8.1
urllib3==1.25.3
wechatpy==1.8.3
xlrd==1.2.0
xlwt==1.3.0
xmltodict==0.12.0
zope.interface==4.6.0

3.新建?routing.py

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

application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
    URLRouter(
        app.routing.websocket_urlpatterns
    )
),
})

4.配置?settings.py

"""
Django settings for itkf project.

Generated by 'django-admin startproject' using Django 3.0.5.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'avamfpy-nj-9q#91nn89^(zjl0s-iu3*◆g◆strpqjxqwerh'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ["*"]

# Application definition

INSTALLED_APPS = [
'simpleui',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app.apps.AppConfig',
'import_export',
'channels',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'itkf.urls'

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates')]
    ,
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ],
    },
},
]
ASGI_APPLICATION = 'itkf.routing.application'
WSGI_APPLICATION = 'itkf.wsgi.application'




# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases

# redis配置
CACHES = {
"default": {
    "BACKEND": "django_redis.cache.RedisCache",
    "LOCATION": "redis://127.0.0.1:6379/",
    "OPTIONS": {
        "CLIENT_CLASS": "django_redis.client.DefaultClient",
        "CONNECTION_POOL_KWARGS": {"max_connections": 100},
        "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
        # "PASSWORD": "密码",
        }
}
}
CHANNEL_LAYERS = {
'default': {
    'BACKEND': 'channels_redis.core.RedisChannelLayer',
    'CONFIG': {
        "hosts": [('127.0.0.1', 6379)],
    },
},
}

DATABASES = {
'default': {
    'ENGINE': 'django.db.backends.mysql',
    'HOST': '127.0.0.1',
    'PORT': '3306',
    'NAME': 'itkf',
    'USER': 'root',
    'PASSWORD': '123456'
}
}
# 开发redis 路径 C:\Program Files\Redis redis-server redis.windows.conf
'''
windows下安装Redis第一次启动报错:

[2368] 21 Apr 02:57:05.611 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
解决方法:在命令行中运行
redis-cli.exe
127.0.0.1:6379>shutdown
not connected>exit
然后重新运行redis-server.exe redis.windows.conf
'''

# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
{
    'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
    'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
    'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
    'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]

# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/

domain = "http://127.0.0.1:8000"

# 图片上传路径
MEDIA_URL = '/'
MEDIA_ROOT = r'D:/itkf/itkfstatic/uploadImage/'

STATIC_URL = '/itkfstatic/'
SIMPLEUI_HOME_INFO = False
# SIMPLEUI 配置
SIMPLEUI_STATIC_OFFLINE = True

STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'itkfstatic'),
)
# 登录页面
LOGIN_URL = '/itkf/admin/login/'

# 权限缓存配置
SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid"  # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"  # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None  # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False  # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600  # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = True  # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False  # 是否每次请求都保存Session,默认修改之后才保存(默认)

weChatWork = {
'corpid': "",
'secret': "",
'sourceFile': "static/source",
'serviceUser_': 'serviceUser_',
'customeUser_': 'customeUser_',
"media_image_url": "/itkfstatic/uploadImage/",
"avatar_image_url": "/itkfstatic/avatar/"
}

5.urls.py 配置

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

urlpatterns = [
path('itkf/admin/', admin.site.urls),

path('itkf/app/', include("app.urls")),
]

6. 项目名称下? >>?__init__.py? 文件配置

import pymysql
#pymysql.version_info = (1, 3, 13, "final", 0)
pymysql.install_as_MySQLdb()

7.应用名称(app) >>?models.py

from django.contrib.auth.models import User
from django.db import models
# Create your models here.
from django.utils.html import format_html
from django.db import models
import datetime
import uuid

from django.db import models
from django.contrib.auth.models import User
# Create your models here.
from django.utils.html import format_html
from django.db.models import IntegerField, Model
from django.core.validators import MaxValueValidator, MinValueValidator
import datetime
import random, os
# Create your models here.
from django.contrib.auth.models import AbstractUser
from django.db import models

ENV_PROFILE = os.getenv("ENV")
if ENV_PROFILE == "test":
import itkf.test_settings as config
elif ENV_PROFILE == "production":
import itkf.prd_settings as config
else:
import itkf.settings as config

corpid = config.weChatWork["corpid"]
sourceFile = config.weChatWork["sourceFile"]
media_image_url = config.weChatWork["media_image_url"]


def rename(newname):
def decorator(fn):
    fn.__name__ = newname
    return fn

return decorator


def newImageName(instance, filename):
filename = '{}.{}'.format(uuid.uuid4().hex, "png")
return filename


# 生成预约订单号

# 用时间生成一个唯一随机数

def random_with_N_digits(n):
range_start = 10 ** (n - 1)
range_end = (10 ** n) - 1
return random.randint(range_start, range_end)


def get_ran_dom():
nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S")  # 生成当前时间
randomNum = random_with_N_digits(3)  # 生成的随机整数n,其中0<=n<=100
if randomNum <= 10:
    randomNum = str(0) ◆ str(randomNum)
uniqueNum = str(nowTime) ◆ str(randomNum)
return uniqueNum



# 应用管理
class agent(models.Model):
name = models.CharField(max_length=225, verbose_name="部门名称", blank=True, default="")
agentid = models.CharField(max_length=225, verbose_name="应用ID", blank=True, default="")
secret = models.CharField(max_length=225, verbose_name="应用密钥", blank=True, default="")
avatar = models.ImageField(max_length=225, verbose_name="部门Logo", blank=True, default="")
conversationTime = models.IntegerField(verbose_name="会话时长(分钟)", default=20)
webhook_url = models.URLField(verbose_name="群机器人地址", default="", blank=True, null=True)

createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
author = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="创建者",
                           related_name="agent_author")
editor = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="修改者",
                           related_name="agent_creator")

@rename("部门Logo")
def showAvatar(self):
    return format_html("", media_image_url,
                       self.avatar)

@rename("详情")
def checkMessage(self):
    return format_html("回复", self.id)

class Meta:
    verbose_name = verbose_name_plural = '部门管理'
    ordering = ['id']

def __str__(self):
    return self.name

# 客服人员
class KF(models.Model):
agent = models.ForeignKey(agent, null=True, on_delete=models.CASCADE, verbose_name="应用名称")
username = models.CharField(max_length=225, verbose_name="姓名", blank=True, default="")
userid = models.CharField(max_length=225, verbose_name="UM", blank=True, default="")
status = models.BooleanField(verbose_name="是否在线", default=False)
avatar = models.ImageField(max_length=225, verbose_name="头像", blank=True, default="")
createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
author = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="创建者",
                           related_name="kf_author")
editor = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="修改者",
                           related_name="kf_creator")

class Meta:
    verbose_name = verbose_name_plural = '在线客服'
    ordering = ['id']

@rename("头像")
def showAvatar(self):
    return format_html("", media_image_url,
                       self.avatar)

def __str__(self):
    return self.username


# 行内员工

def randomSign():
switch = {
    0: "只要还有明天,今天就永远是起跑线.",
    1: "只要还有明天,今天就永远是起跑线.",
    2: "只要还有明天,今天就永远是起跑线."
}
return switch[0]


class userList(models.Model):
agent = models.ForeignKey(agent, null=True, on_delete=models.CASCADE, verbose_name="应用名称")
username = models.CharField(max_length=225, verbose_name="姓名", blank=True, default="")
userid = models.CharField(max_length=225, verbose_name="UM", blank=True, default="")
avatar = models.ImageField(max_length=225, verbose_name="头像", blank=True, default="")
sign = models.CharField(max_length=225, verbose_name="个性签名", blank=True, default=randomSign)

ISLEAD_CHOICES = ((0, ''), (1, ''),)
islead = models.IntegerField(choices=ISLEAD_CHOICES, verbose_name="等级", default=1)
createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
author = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="创建者",
                           related_name="userlist_author")
editor = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="修改者",
                           related_name="userlist_creator")

@rename("头像")
def showAvatar(self):
    return format_html("", media_image_url,
                       self.avatar)

class Meta:
    verbose_name = verbose_name_plural = '用户列表'
    ordering = ['id']

def __str__(self):
    return self.username


# 接受的消息

class Message(models.Model):
ToUserName = models.CharField(max_length=225, verbose_name="接受者", blank=True, default="")
FromUserName = models.CharField(max_length=225, verbose_name="发送者", blank=True, default="")
CreateTime = models.DateTimeField(verbose_name="发送时间", blank=True, default=None)
MsgId = models.CharField(max_length=225, verbose_name="消息ID", blank=True, default="")
AgentID = models.CharField(max_length=225, verbose_name="部门名称", blank=True, default="")
MsgType = models.CharField(max_length=225, verbose_name="消息类型", blank=True, default="")
content = models.TextField(max_length=2000, verbose_name="消息内容", blank=True, default="")
userList = models.ForeignKey('userList', null=True, to_field="id", on_delete=models.CASCADE)
createDateTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
author = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="创建者",
                           related_name="message_author")
editor = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="修改者",
                           related_name="message_creator")

class Meta:
    verbose_name = verbose_name_plural = '所有消息'
    ordering = ['id']

def __str__(self):
    return self.FromUserName





# 员工服务
class staffService(models.Model):
agent = models.ForeignKey('agent', null=True, on_delete=models.CASCADE, verbose_name="应用名称")
title = models.CharField(max_length=225, verbose_name="标题", blank=True, default="")
avatar = models.ImageField(max_length=225, verbose_name="头像", blank=True, default="")
desc = models.TextField(max_length=500, verbose_name="描述", default="", blank=True, null=True)

welcomeText = models.TextField(max_length=2000, verbose_name="<