Skip to content

Commit 34f34e6

Browse files
authored
Merge pull request ictar#38 from ictar/draft
add translated for "How to Extend Django User Model" and "Django Chan…
2 parents 2da459d + 3f4481c commit 34f34e6

4 files changed

Lines changed: 1067 additions & 0 deletions
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
原文:[Django Channels and Celery Example](http://vincenttide.com/blog/1/django-channels-and-celery-example/)
2+
3+
---
4+
5+
在本教程中,我将探讨如何建立一个Django Channels项目来与Celery协同工作,以及在任务开始和结束的时候,即时通知。Django Channels使用WebSockets来启用服务器和浏览器客户端之间的双向通信。假设读者已经熟悉如何建立一个普通的Django项目,我们将只覆盖Channels和Celery相关的部分。
6+
7+
你可以在[这里找到Github repo](https://github.com/VincentTide/django-channels-celery-example)以及位于<http://tasker.vincenttide.com>的一个相同部署。注意,该部署包含了在此教程不包括的一些额外的内容,例如取消功能。示例部署的前端还运行React库,而我们在这个演示将只使用JavaScript。
8+
9+
首先,让我们安装一些需要的依赖。我们将需要一个Channels层后端,Channels用来传递和存储数据。我们还将需要一个Celery broker传输后端。事实证明,我们可以为这些任务同时使用Redis,因此Redis就是我们所用的。
10+
11+
```python
12+
13+
# Add Chris Lea’s redis ppa - he maintains the ppa for many open source projects
14+
$ sudo add-apt-repository ppa:chris-lea/redis-server
15+
$ sudo apt-get update
16+
$ sudo apt-get install redis-server
17+
18+
# Now check that redis-server is up and running
19+
$ redis-cli ping
20+
# PONG
21+
22+
```
23+
24+
在一个virtualenv内设置一个新的Django项目,然后安装以下库:
25+
26+
```python
27+
28+
$ pip install django
29+
$ pip install channels # the channels library
30+
$ pip install asgi_redis # the redis channel layer we are using
31+
$ pip install celery # Celery task queue
32+
33+
```
34+
35+
让我们先看看settings.py文件。
36+
37+
```python
38+
39+
# Add our new app to installed apps
40+
INSTALLED_APPS = [
41+
#
42+
‘jobs’,
43+
]
44+
45+
# Channels settings
46+
CHANNEL_LAYERS = {
47+
"default": {
48+
"BACKEND": "asgi_redis.RedisChannelLayer", # use redis backend
49+
"CONFIG": {
50+
"hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')], # set redis address
51+
},
52+
"ROUTING": "django_channels_celery_tutorial.routing.channel_routing", # load routing from our routing.py file
53+
},
54+
}
55+
56+
# Celery settings
57+
BROKER_URL = 'redis://localhost:6379/0' # our redis address
58+
# use json format for everything
59+
CELERY_ACCEPT_CONTENT = ['json']
60+
CELERY_TASK_SERIALIZER = 'json'
61+
CELERY_RESULT_SERIALIZER = 'json'
62+
63+
```
64+
65+
首先,添加我们的新app到INSTALLED_APPS列表中。Channels设置只是告诉Channels我们所使用的后端,在这里,是Redis。ROUTING选项告诉Channels到哪里找我们的WebSockets路径,这里是routing.py文件。Celery设置告诉Celery到哪里找我们的broker,以及对所有东西,我们想要使用json格式。
66+
67+
现在,看看routing.py文件:
68+
69+
```python
70+
71+
from channels import route
72+
from jobs import consumers
73+
74+
channel_routing = [
75+
# Wire up websocket channels to our consumers:
76+
route("websocket.connect", consumers.ws_connect),
77+
route("websocket.receive", consumers.ws_receive),
78+
]
79+
80+
```
81+
82+
这里,我们只是为连接和接收消息设置了处理函数。我们还可以添加一个函数来处理断开连接消息,但对我们来说,并不需要。我们告诉Channels在我们的jobs/consumers.py文件中查找函数。
83+
84+
这里是consumers.py文件的主要部分:
85+
86+
```python
87+
88+
@channel_session
89+
def ws_connect(message):
90+
message.reply_channel.send({
91+
"text": json.dumps({
92+
"action": "reply_channel",
93+
"reply_channel": message.reply_channel.name,
94+
})
95+
})
96+
97+
98+
@channel_session
99+
def ws_receive(message):
100+
try:
101+
data = json.loads(message['text'])
102+
except ValueError:
103+
log.debug("ws message isn't json text=%s", message['text'])
104+
return
105+
106+
if data:
107+
reply_channel = message.reply_channel.name
108+
109+
if data['action'] == "start_sec3":
110+
start_sec3(data, reply_channel)
111+
112+
```
113+
114+
在我们的ws_connect函数中,我们将只是把客户端的回复通道(reply channel)地址回传。回复通道是一个分配给每一个连接到我们的websockets服务器的浏览器客户端的唯一地址。可以从message.reply_channel.name中检索到该值,并且可以保存或传递该值到另一个函数,例如Celery 的task,这样的话,就可以将消息发回去。事实上,这就是我们将要做的事。message.reply_channel.send是Channels为我们提供的,用来回复到同一个客户端的一个方便的快捷方式。如果你仅有reply_channel名,那么你将必须使用以下方法来发送消息:
115+
116+
```python
117+
118+
Channel(reply_channel_name).send({
119+
"text": json.dumps({
120+
"action": "started",
121+
"job_id": job.id,
122+
"job_name": job.name,
123+
"job_status": job.status,
124+
})
125+
})
126+
127+
```
128+
129+
在我们的ws_receive函数中,我们根据action参数来看看客户端想要我们做什么。如果你想要做不同的事,那么可以有多个action指令。在我们的例子中,我们只有一个指令,它运行一个名为start_sec3的函数。start_sec3只是休眠3秒,然后回复它已经完成的消息给客户端。注意,我们传递了reply_channel地址,因此它知道发送响应到哪。
130+
131+
最后一个重要的块是客户端侧的javascript处理函数。
132+
133+
```python
134+
135+
$(function() {
136+
// When we're using HTTPS, use WSS too.
137+
var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
138+
var ws_path = ws_scheme + '://' + window.location.host + '/dashboard/';
139+
console.log("Connecting to " + ws_path)
140+
var socket = new ReconnectingWebSocket(ws_path);
141+
142+
socket.onmessage = function(message) {
143+
console.log("Got message: " + message.data);
144+
var data = JSON.parse(message.data);
145+
146+
// if action is started, add new item to table
147+
if (data.action == "started") {
148+
var task_status = $("#task_status");
149+
var ele = $('<tr></tr>');
150+
ele.attr("id", data.job_id);
151+
var item_id = $("<td></td>").text(data.job_id);
152+
ele.append(item_id);
153+
var item_name = $("<td></td>").text(data.job_name);
154+
ele.append(item_name);
155+
var item_status = $("<td></td>");
156+
item_status.attr("id", "item-status-"+data.job_id);
157+
var span = $('<span class="label label-primary"></span>').text(data.job_status);
158+
item_status.append(span);
159+
ele.append(item_status);
160+
task_status.append(ele);
161+
}
162+
// if action is completed, just update the status
163+
else if (data.action == "completed"){
164+
var item = $('#item-status-' + data.job_id + ' span');
165+
item.attr("class", "label label-success");
166+
item.text(data.job_status);
167+
}
168+
};
169+
170+
$("#taskform").on("submit", function(event) {
171+
var message = {
172+
action: "start_sec3",
173+
job_name: $('#task_name').val()
174+
};
175+
socket.send(JSON.stringify(message));
176+
$("#task_name").val('').focus();
177+
return false;
178+
});
179+
});
180+
181+
```
182+
183+
这里,我们首先创建websockets对象,然后用socket.onmessage函数来处理为每个websockets消息我们应该做的事。如果action参数的值是“started”,那么我们将添加一个新的条目到表格中。如果action是completed,我们只会修改对应的列状态为已完成。
184+
185+
而表单则是发送一个websockets消息到服务器,告诉它运行action “start_sec3”。
186+
187+
188+
要看完整的项目文件,请访问[Github repo](https://github.com/VincentTide/django-channels-celery-example)。要运行Github repo代码,先确保你安装了Redis,然后运行以下命令:
189+
190+
```python
191+
192+
pip install -r requirements.txt
193+
python manage.py makemigrations
194+
python manage.py migrate
195+
python manage.py runserver # Start daphne and workers
196+
celery worker -A example -l info # Start celery workers
197+
198+
```
199+
200+
这会启动部署服务器,地址为`http://localhost:8000`。再一次说明,你可以在http://tasker.vincenttide.com上找到一个类似的部署。
201+
202+

Django/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@
1919
- [在Django中,如何为提高页面加载速度优化图像](./在Django中,如何为提高页面加载速度优化图像.md)
2020

2121
关于如何通过优化图像来提高网页页面加载的Django和Python技巧和窍门。
22+
23+
- [Django Channels和Celery示例](./Django Channels和Celery示例.md)
24+
25+
在本教程中,我将探讨如何建立一个Django Channels项目来与Celery协同工作,以及在任务开始和结束的时候,即时通知。Django Channels使用WebSockets来启用服务器和浏览器客户端之间的双向通信。假设读者已经熟悉如何建立一个普通的Django项目,我们将只覆盖Channels和Celery相关的部分。
26+
27+
- [如何扩展Django User模型](./如何扩展Django User模型.md)

0 commit comments

Comments
 (0)