django+channel+vue 前后端分离 实现推送消息给前端和我的疑惑

原因:最新在写数据仓,其中一项是后端实时返回订单状态给前端,前端就一条一条显示。所有使用了django 推荐的websocket 插件channels

本人也是刚学的,做个记录,新手勿喷

版本号:和配置
django==2.2.2
channels == 3.0.4
channels-redis == 3.4.0
用的redis 是linxu ,windows版本不够(会报错的 兄弟们),直接用linux
要先安装 channels 和 channels-redis

pip3 install  channels  
pip3 install  channels-redis    

然后再setting 里面配置

setting

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework.authtoken',
    'rest_framework',
    'jojoapp',
    'corsheaders',
    'channels'
]

PS: 请注意 xybbcmysite.asgi.application  
         其中xybbcmysite 是我的项目名,
         asgi 是我项目下的文件 
         application  是我文件下的方法名


ASGI_APPLICATION = "xybbcmysite.asgi.application"
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            # "hosts": [('127.0.0.1', 6379)],
            "hosts": ["redis://:host:6379/3"],     PS:  3 是你选择的哪个数据库
        },
    },
}


代码目录

在这里插入图片描述
和网络上教程一样,现在项目下创建一个文件 asgi 文件

asgi.py 文件内容

import os
import django
from channels.auth import AuthMiddlewareStack
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouter,URLRouter
import  jojoapp.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'xybbcmysite.settings')
django.setup()


# ProtocolTypeRouter 检查数据类型,检查这个请求是HTTP 还是ws, 进行一个分配的,简单理解哈哈哈
application = ProtocolTypeRouter({
  "http": AsgiHandler(),
  # Just HTTP for now. (We can add other protocols later.)
  "websocket": AuthMiddlewareStack(
        URLRouter(
            jojoapp.routing.websocket_urlpatterns
			
			
			注意:jojoapp 是我的APP名(别告诉我你不知道)
			routing 是我APP名下的文件名(目录结构里有)
			websocket_urlpatterns  是 routing 文件下的 参数
			
        )
    ),
})

routing.py

这里一定要用 re_path ,不要用path, 网上版本的都是2的,3.0的一定是re_path。我换成path 的时候,根本就匹配不到

# 路由分发
from  django.urls import  re_path

from . import  consumers,consumers2

from . import  outconsumers
websocket_urlpatterns = [
    re_path(r'ws/xyjojo/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
    # re_path(r'ws/xyjojo/(?P<room_name>\w+)/$', consumers2.ChatConsumer.as_asgi(), name='获取订单状态')
]

我这里有两个 文件 consumers 和consumers2
这两个文件都相当于 django 里面的试图view, 如果你有多个ws 的需求,就多建几个这样的文件
consumers 是异步的
consumers2 是同步的

consumers

import json
import  time
import  asyncio

from channels.generic.websocket import AsyncWebsocketConsumer

from  .src import  orderrequest
from  .scmenum import *
functtime = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())


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

        # 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
        )

    # Receive message from WebSocket
    async def receive(self, text_data):

        text_data_json = json.loads(text_data)

        message = text_data_json['message']

            # Send message to room group

        await self.channel_layer.group_send(

            self.room_group_name,
            {
                'type': 'chat.message',  # 这里的type 实际上就是 下面的chat_message自定义函数
                'message': message
            }
        )



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

        if orderno[0:1] !="PO" or orderno[0:1] !="O1":
            if  await  orderrequest.get_channel_id_and_channelorderid(channelno=orderno,room_group_name=self.room_group_name,type=1) ==2001:

                await  orderrequest.get_channel_id_and_channelorderid(channelno=orderno, room_group_name=self.room_group_name, type=2)
            else:

                await self.send(text_data=json.dumps({
                    'message': f"{functtime}{OrderStatus.orderstate.value}",
                }))


        else:
            await  self.send(text_data=json.dumps({
                "message":f"{functtime}请使用渠道单号发货"
            }))





    async def send_message(self, event):
                message = event['message']
                # Send message to WebSocket
                await self.send(text_data=json.dumps({
                    'message': message,


                }))



consumers2

import json
import  time
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from  .src import  orderrequest
from  .scmenum import *

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    # 从websocket 接受消息
    def receive(self, text_data):

        text_data_json = json.loads(text_data)
        message = text_data_json['message']

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

    # Receive message from room group
    # 将消息返回给对应得组
    def chat_message(self, event):
        orderno = event['message']

        if orderno is None or orderno is "":
            self.send(text_data=json.dumps({
                'message': OrderStatus.statusnull.value
            }))
            self.close()
            return  None
        else:
            message = orderrequest.get_channel_id_and_channelorderid(channelno=orderno,room_group_name=self.room_group_name)
            # self.send(text_data=json.dumps({
            #     'message': message
            # }))


    def order_message(self, event):
        message = event['message']
        self.send(text_data=json.dumps({
            'message':message
        }))




注意: 不管在同步还是在异步中都存在一段代码如下

def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
	async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
           	# 两种写法 可以匹配下面的方法
           	 #  'type': 'chat.message', 
                'type': 'chat_message', 
                'message': message
            }
        )
      def chant_message(self, event):
        message = event['message']
        self.send(text_data=json.dumps({
            'message':message
        }))     

官方已经说过了,我就复习一下。 这里的type 指的的 发送信息给相应组的函数,也就是说文件里一定存在于一个 函数名叫  chat_message的函数

让我想想还有啥问题

有的情况会在消费之外发送消息给通道层,怎么办,官方已经给出了答案

请注意这段代码只能在同步代码中使用,不能在异步中使用

from asgiref.sync import async_to_sync
async_to_sync(channel_layer.group_send)("chat", {"type": "chat.force_disconnect"})

异步使用如下

await channel_layer.group_send(
        chat_name,
        {"type": "chat.system_message", "text": announcement_text},
    )

消费之外的代码如下:文件目录

在这里插入图片描述

下面的方法是异步的,异步方法都是async 开头, 执行异步方法是await 开头

如果想用官方的,在 消费之外的函数不加上 async ,和await ,这个时候就能使用 同步的代码了

async  def get_channel_id_and_channelorderid(channelno,BBMALL_URL=BBMALL_URL,room_group_name=None,type=None):
    '''
    根据渠道单查询channelId 和channelOrderId  在bbmall 数据库中查找相应的平台订单和采购单号
    :param room_group_name:  组名,用于区分组,进行不同组信息发送
    :param channelno: 渠道单号
    :param BBMALL_URL: url
    :return: channelOrderId  channelId
    '''
    headers = {
        'Authorization': '',
        'route': '',
        'Origin': 'http://xingyun.test.bbmall.xyb2b.com.cn',
        'Content-Type': 'application/json'
    }
    channeldetail_response =  requests.request("POST", url=BBMALL_URL+ await  query_channelorderno_url(),
                                     headers=headers,data=await query_channelorderno_params(channelno))

  
    if  len(channeldetail_response.json()['data']['records'])>0:
        # channelId = channeldetail_response.json()['data']['records'][0]["channelId"]
        channelOrderId = channeldetail_response.json()['data']['records'][0]["channelOrderId"]


		PS :  下面的代码{"type": "send_message",  send_message:你的文件里一定存在一个这样的方法

        await channel_layer.group_send(room_group_name, {"type": "send_message",
                                                                   "message": f"{getstatustime}-{channelOrderId}-{OrderStatus.stat_con_mysql.value}"})


        purchase_order =  await  connet_mysql(channelOrderId=channelOrderId,room_group_name=room_group_name)
        return  await purchase_order_status(orderNo=purchase_order,type=type,room_group_name=room_group_name)

    else:
        await channel_layer.group_send(room_group_name, {"type": "send_message",
                                                                  "message": f"{getstatustime}-{OrderStatus.stat_query_error.value}"})

前端代码,前端代码

 created() {
    let nowdate = new Date();
    let hh = nowdate.getHours();
    let mf =
      new Date().getMinutes() < 10
        ? "0" + new Date().getMinutes()
        : new Date().getMinutes();
    let ss =
      new Date().getSeconds() < 10
        ? "0" + new Date().getSeconds()
        : new Date().getSeconds();
    let gettime = hh.toString() + mf.toString() + ss.toString();
    console.log(gettime);
    createSocket(`ws://127.0.0.1:8000/ws/xyjojo/${gettime}/`);
  },

运行原理

vue的请求—> asgi,判断是HTTP 还是ws. 是ws ----> 进入routing-> 开始匹配路由–>查看相应的consumers文件,开始执行ws

我的问题(已经解决的使用super 直接调用父类的send方法)

各位大佬,虽然这个已经写完了。
我期望的结果:订单在不同的域中,状态是不一样的,前端显示的应该一条一条的显示后端的结果。
实际结果: 等代码全部运行完成,”Send message to WebSocket“ 这个方法会一直在循环,循环之前我在 channel_layer.ground_send() 的返回,将全部信息一下返回,没有做到一条一条的显示,就算我在中间加了 时间等待,返回数据的时间都是一摸一样。怎么解决

这个问题算是解决了吧,应该只能算符合预期,以下是解决方案

await  orderrequest.get_channel_id_and_channelorderid(self, channelno=orderno)

其中的self 代表着consumers,我在consumers中,将self传给了其他文件里的函数然后执行下面的代码

await  self.send(text_data=json.dumps({
                "message":"进入发货准备o(=•ェ•=)m"
            }))
            await asyncio.sleep(0.1)

就可以达到预期,遇到send 这个方法直接返回给前端,但是必须要加上 await asyncio.sleep(0.1) 这航。如果不加,就返回不了。哎,在慢慢整吧。

问题解决了

当初没有仔细看官方文档,官方文档已经给出了使用方法,就像下面一样操作可以
在这里插入图片描述

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python是一种流行的编程语言,有非常丰富的库和框架可供使用。而Django是一个基于Python的开发框架,用于快速搭建Web应用程序。Vue是一种流行的JavaScript框架,用于构建用户界面。 当我们要搭建一个前后端分离的项目时,可以使用Python的Django框架和Vue框架来实现。下面是大致的步骤: 1. 对于后端,我们可以使用Django框架来搭建RESTful API。首先,创建一个新的Django项目并设置好相关配置。然后,定义数据模型和数据库表,可以使用Django的ORM(对象关系映射)来简化数据库操作。接下来,创建视图函数来处理不同的API请求,可以通过序列化器将模型数据转换为JSON格式进行传输。最后,设置URL路由映射和权限控制,确保API的安全性和可访问性。 2. 对于前端,我们可以使用Vue框架来构建用户界面。首先,创建一个新的Vue项目并安装所需的依赖项。然后,创建组件来管理不同的页面和功能模块。在组件中,可以使用Vue的数据绑定和计算属性等功能来简化数据处理和状态管理。同时,可以使用Vue Router来管理页面之间的导航和路由,以及使用axios库来发送API请求和接收响应。 3. 在前后端之间进行通信时,前端通过axios发送HTTP请求到后端的API接口,后端处理请求并返回相应的数据。前端可以根据接口的返回结果来更新界面和展示数据。 总的来说,使用Python的Django框架和Vue框架可以很好地实现前后端分离的项目。Django提供了强大的后端支持,可以快速搭建API接口,而Vue框架则提供了便捷的前端开发环境,可以轻松构建用户界面。通过这种搭配,我们可以高效地开发出功能完善、交互流畅的Web应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值