关键时刻,第一时间送达!
在本教程中,我们会使用Django Channel创建一个实时更新用户登录登出列表的应用程序。
使用WebSockets在Django Channel框架下管理客户端与服务器之间的通讯,每当一个用户获得登录许可,该事件会被广播给每一个在线用户。每一位用户不用重新登录就可以在屏幕上自动获取实时发生的变化。
注意:我们建议你在开始阅读本教程之前对Django有过使用经验,因而你应该熟悉WebSockets的一些概念。
工具
我们制作该应用程序需要使用如下工具:
Python (v3.6.0)
Django (v1.10.5)
Django Channels (v1.0.3)
Redis (v3.2.8)
目标
完成本教程之后,我们能够......
1.通过Django Channel将Web Sockets添加到Django的工程项目中
2.在Django和一个Redis服务器之间创建一个简单的连接
3.实现基本的用户登录验证
4.每当一个用户登录或者登出的时候都会通过Django触发反馈操作
准备工作
首先,创建一个新的虚拟环境独立地来安装项目的一些依赖。
然后,安装Django,Django Channel和ASGI Redis,然后创建一个新的Django项目和应用程序。
注意:在实践这个教程的过程中,我们会创建大量不同的文件和文件夹。如果你在什么地方卡住了,请参考项目仓库的文件结构。
接着,下载并安装Redis。如果你的电脑是Mac系统,我们建议使用Homebrew来进行安装:
接着,另外启动一个终端来运行Redis服务器程序,请确认程序运行默认端口号是6379,因为当我们要求Django与Redis交互的时候这个端口至关重要。完成如下更新项目setting.py文件中的INSTALLED_APPS之后初始化设置就完成了:
然后通过设置默认的BACKEND和ROUTING来配置CHANNEL_LAYERS
正如实验所需,这里使用Redis作为后端
WebSockets 101
通常情况下,Django使用HTTP协议来完成客户端与服务器的通讯:
1.客户端给服务器发送一个HTTP request
2.由Django来分析这个request,提取其中的URL,然后配对成一个视图
3.由视图处理请求之后将一个HTTP response返回给客户端
说明:和HTTP协议不同的是,WebSockets协议允许双向数据传输,也就是说服务器可以在用户不请求的情况下将数据传输给客户端,而HTTP协议中只有发送request的客户端才会接收到response。使用了WebSockets之后,服务器可以同时和多台客户端进行数据交互。在本教程中,我们使用前缀ws://来传送WebSockets消息,以示区分于http://
注意:在入坑之前,请迅速浏览Channels Concepts的文档
Consumers and Groups
创建第一个consumer用来处理客户端与服务器之间的基础连接,在目录example_channel/example目录下新建一个文件叫作consumers.py,内容如下:
Consumers与Django的视图相配对。每当有用户连接到我们的应用程序,就会被添加到’users’组内,并且会接收到服务器发送的信息。当客户端关闭与我们的应用程序的连接时,这个通道就会从组内移除,用户将不再接收到来自服务器的信息。接下来让我们设置路由,这个操作和Django URL配置几乎相同,在example_channels目录下创建一个叫作routing.py的文件,将下述代码添加进去:
因此我们用channel_routing代替了urlpatterns,用route()代替了url()。此时我们发现comsumer函数已经和WebSockets关联。
模板
我们来写一些可以通过WebSocket来与服务器进行数据传输的HTML代码,在example_channels/example目录下递归创建templates/example,最后构成目录”example_channels/example/templates/example”。HTML代码如下:
现在,客户端已经用WebSocket打开一个与服务器的连接,控制台会打印确认信息。
视图
1.在example_channels/example/views.py文件中设置一个支持Django视图来递交template
2.将URL添加到example_channels/example/urls.py文件中:
3.在example_channels/example_channels/urls.py文件中同样更新URL
测试
测试命令如下:
注意:可以在两个不同的终端窗口运行python manage.py runserver –noworker和python manage.py runworker,来测试端口和worker服务作为独立进程的运行结果。两条命令都可实现,此时当你访问http://localhost:8000/时你会看到终端打印的连接信息如下:
用户验证
既然我们已经证实我们可以打开一个连接,那么我们下一步就是解决用户验证问题。记得我们的任务是:我们需要一种方式,使得每一个登录到该应用程序的用户都能够看到一个包含所有用户的列表。那么我们首先需要给用提供一个创建账号和登录的方式。从创建一个简单的登录页面开始,实现用户通过用户名和密码进行验证的登录方式。在example_channels/example/templates/example目录下创建一个叫作log_in.html的文件,内容如下:
然后,更新example_channels/example/views.py如下:
Django的表单结构支持常用的认证功能,我们使用AuthenticationForm来处理用户登录。这个表单结果检查用户提交的用户名和密码,然后如果可以找到一个有效用户,就返回用户对象。登录有效的用户对象之后将页面重定向到主页,为了用户也可以能够登出该应用程序,因此我们又创建了一个登出视图来实现这一功能,登出后将用户对象回退到登录界面。更新example_channels/example/urls.py文件如下:
我们同时还需要一种方式来创建新用户,和创建登录页面一样,在文件夹example_channels/example/templates/example中创建一个叫作sign_up.html的新文件,由此来创建一个注册页面,该文件内容如下:
注意到,在登录页面有一个指向注册页面的链接,同时注册页面也有一个回退到登录页面的指针。将下列函数添加到视图中:
我们使用另一个内建表单结构来创建用户对象成功判断表单有效后,我们将页面重定向到登录页面,通过如下方式导入表单结构:
再次更新example_channels/exaple/urls.py文件如下:
此时我们需要创建一个用户对象。运行服务器程序之后,用浏览器访问http://localhost:8000/sign_up,用有效的用户名和密码填写表单,并提交数据来创建第一个用户。
注意:用户名michael,密码johnson123
sign_up视图将我们重定向到log_in视图,在该视图下我们可以认证我们新创建的用户对象。我们登录之后,可以测试新的认证视图。使用注册表单创建几个新的用户对象,为下一部分的工作做准备。
登录提醒
我们已经能够实现最基本的认证功能,但是我们还需要显示包含用户的列表,我们要求服务器告诉用户组该用户什么时候登录,什么时候登出。然后编写consumer函数实现,用户连接之后或者用户断开之前立刻发送一条信息。这些信息数据包括用户的用户名和运行状态,更新example_channels/example/consumers.py文件如下:
注意:我们已经将装饰器添加到函数,由此可以从Django获得用户对象。同时所有信息都必须是序列化JSON格式,因此我们必须把数据转换成JSON字符串,接着按如下所示更新example_channels/example/templates/example/user_list.html文件:
在我们的主页上,扩展了用户列表来存放一个列表的用户,我们存储每一个用户的用户名作为一个数据属性,由此可以容易地在DOM中找到用户记录。我们还针对WebSocket添加了一个事件监听器,因此可以处理来自服务器的信息。每当收到一条信息,我们就分析JSON数据,找到所指定用户对应的
元素,并且更新该用户的状态。
由于Django是不监视用户是否登录的,因此我们需要创建一个简单的模型来实现监视功能。在example_channels/example/models.py文件中,用一个连接到user模块的一对一连接来创建一个LoggedInUser模型:
每当用户登录,应用程序就会创建一个LoggedInUser实例,并且用户登出的时候,应用程序删去该实例。
进行架构迁移的时候可以迁移数据库适应变化。
然后,在example_channels/example/views.py文件中,更新用户列表视图,可以恢复用户列表
如果一个用户对象有一个关联的LoggedInUser对象,那么我们记录这个用户的状态是’Online’,相反则用户对象处于’Offline’状态。我们还需要在用户列表和登出视图中添加一个@login_required装饰器来达到仅限制于注册用户可以获得。
此时用户可以登录或者登出,并且由此触发服务器向客户端发送消息,但是我们无法知道当一个用户第一次登录的时候哪些用户已经被记录。用户只能在另一个用户状态发生改变的时候看到更新,这里就是LoggedInUser起作用的地方,但是我们需要一种方式当一个用户登录的时候创建一个LoggedInUser实例,然后在登出的时候进行销毁。
Django库有一个叫作signals的特色,会在特定事件发生的时候进行广播通知。应用程序可以监听这些通知并且按照广播内容来做出响应,我们可以利用两个较有用的内建signal(包括user_logged_in和user_logged_out)来处理LoggedInUser的行为。在example_channels/example目录下创建一个新文件signals.py,内容如下:
我们应该在应用程序配置中获得这些signals,配置example_channels/example/apps.py文件如下:
更新example_channels/example/__init__.py文件如下:
正确性检查
现在我们完成了代码编写,让一些用户来连接我们的服务器,从而测试我们的应用程序。
运行Django服务器,用户登录并访问主页。我们可以看到包含应用程序中所有用户的列表,其他用户都处于’offline’状态。然后打开一个新的匿名窗口,以另外一个用户身份登录,该用户一登录浏览器就将用户的状态为’Online’。从匿名窗口,我们也可以看到刚刚登录的用户也处于’Online’状态。我们可以通过在不同的设备上让不同的用户登录登出来测试WebSockets的健壮性。
通过在客户端观察开发控制台信息并且可以在终端查看服务器运行情况。我们可以确认,当用户登录的时候WebSockets连接会形成,而用户登出的时候则会被销毁。
注意:你可以使用ngrok将本地服务器安全地挂在互联网上。这样你就可以使用不同的移动终端比如手机,平板等,访问本地服务器了。
结束语
这片教程涵盖了许多东西,包括Django Channels,WebSockets,用户认证,信号,还有一些前端开发知识。主旨是想说明:Channels扩充了传统的Django应用程序的功能,能够从服务器通过WebSockets将信息推送给多组用户。它的功能非常强大。
想想此类的一些应用,比如我们可以创建聊天室,创建多人游戏,创建能够实现用户实时交流的协作应用程序。甚至我们可以用WebSockets来优化一些常规任务,比如:服务器会在完成任务之后给客户端发送状态更新,而不是时不时地查看服务器,看一看一个长时运行的任务是否完成。
这个教程只是扒了一些Django Channel的一些简单用法,查看Django Channel文档可以查看其它可以实现的功能。
读者可以从https://github.com/realpython/django-example-channels仓库获取项目的最终代码。拜拜!!
来自:python程序员
译者:thwcww
英文原文:https://realpython.com/blog/python/getting-started-with-django-channels/
Python开发整理发布,转载请联系作者获得授权
领取专属 10元无门槛券
私享最新 技术干货