首页 > Python基础教程 >
-
带你认识 flask 后台作业(4)
import json
from flask import render_template
from app.email import send_email
# ...
def export_posts(user_id):
try:
# ...
send_email('[Microblog] Your blog posts',
sender=app.config['ADMINS'][0], recipients=[user.email],
text_body=render_template('email/export_posts.txt', user=user),
html_body=render_template('email/export_posts.html', user=user),
attachments=[('posts.json', 'application/json',
json.dumps({'posts': data}, indent=4))],
sync=True)
except:
# ...
只是其实对send_email()
函数的调用。附件被定义为一个元组,其中有三个元素被传递给瓶邮件的Message
对象的attach()
方法。元组中的第三个元素是附件内容,它是用Python中的json.dumps()
函数生成的。
这里引用了一对新模板,它们以纯文本和HTML格式提供电子邮件正文的内容。这是文本模板的内容:
app / templates / email / export_posts.txt:更新用户动态文本邮件模板
Dear {{ user.username }},
Please find attached the archive of your posts that you requested.
Sincerely,
The Microblog Team
这是HTML版本的邮件模板:
app / templates / email / export_posts.html:更新用户动态HTML邮件模板
<p>Dear {{ user.username }},</p>
<p>Please find attached the archive of your posts that you requested.</p>
<p>Sincerely,</p>
<p>The Microblog Team</p>
12
应用中的导出功能
剩下的就是将这个功能连接到应用,刹车用户发起请求并通过电子邮件发送用户动态给他们
下面是新的export_posts
视图函数:
app / main / routes.py:导出用户动态路由和视图函数
@bp.route('/export_posts')
@login_required
def export_posts():
if current_user.get_task_in_progress('export_posts'):
flash(_('An export task is currently in progress'))
else:
current_user.launch_task('export_posts', _('Exporting posts...'))
db.session.commit()
return redirect(url_for('main.user', username=current_user.username))
该功能首先检查用户是否有未完成的任务,并在这种情况下只是闪现消息。对同一用户同时执行两个发起任务是没有意义的,可以避免。我可以使用前面实现的get_task_in_progress()
方法来检查这种情况
如果一个用户没有正在运行的导出任务,则调用launch_task()
来启动它。第一个参数是将传递给RQ worker的函数的名称,改为为app.tasks.
。第二个参数只是一个友好的文本描述,将会显示给用户。这两个值都会被写入数据库中的任务对象。该函数以重定向到用户个人主页结束
我认为最合适的地方是在用户个人主页,只有在用户查看他们自己的主页时,链接在“编辑个人资料”链接下面显示:
app / templates / user.html:用户个人主页的导出链接
...
<p>
<a href="{{ url_for('main.edit_profile') }}">
{{ _('Edit your profile') }}
</a>
</p>
{% if not current_user.get_task_in_progress('export_posts') %}
<p>
<a href="{{ url_for('main.export_posts') }}">
{{ _('Export your posts') }}
</a>
</p>
...
{% endif %}
此链接的渲染是有条件的,因为我不希望它在用户已经有发起任务执行时出现。
如果你想尝试一下,你可以按如下方式启动应用和RQ worker:
-
确保Redis正在运行
-
:一个终端窗口,启动至少一个RQ worker实例。本处你可以运行命令
rq worker microblog-tasks
-
再打开另一个终端窗口,使用
flask run
(记得先设置FLASK_APP
变量)命令启动Flask应用
13
进度通知
为了完善这个功能,我想在后台任务运行时提醒用户任务完成的进度。在浏览Bootstrap组件选项时,我决定在导航栏的下方使用一个Alert组件。横条。我用蓝色的警报框来渲染闪现的消息。现在我要添加一个绿色的警报框来显示任务进度。样式如下:
app / templates / base.html:基础模板中的导出进度Alert组件
...
{% block content %}
<div class="container">
{% if current_user.is_authenticated %}
{% with tasks = current_user.get_tasks_in_progress() %}
{% if tasks %}
{% for task in tasks %}
<div class="alert alert-success" role="alert">
{{ task.description }}
<span id="{{ task.id }}-progress">{{ task.get_progress() }}</span>%
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% endif %}
...
{% endblock %}
...
外部条件在用户未登录时跳过所有与Alert相关的标记。而对于已登录的用户,我通过称为创建的get_tasks_in_progress()
方法来获取当前的任务列表。在当前版本的应用中,我最多只能得到一个结果,因为我可以多个替换任务同时执行,但将来我可能要支持可以共存的其他类型的任务,所以以通用的方式渲染Alert可以节省我以后的时间。
对于每项任务,我都会在页面上渲染一个警报元素。警报的颜色由第二个CSS样式控制,本处是alert-success
,而在闪现消息是alert-info
,引导文档所有游戏有关警报的HTML结构的详细信息。警报文本包括存储在Task
模型中的description
细分,后面跟着完成百分比。
被百分比封装在具有id
属性的<span>
元素中。原因是我要在收到通知时用的JavaScript刷新百分比。我给任务ID附加末尾-progress
来构造id
属性。当有通知到达时,通过其中的任务ID,我可以很容易地使用#<task.id>-progress
选择器找到正确的<span>
元素来更新。
如果您此时进行尝试,则每次导航到新页面时都会看到“静态”的进度更新。您可以注意到,在启动导出任务后,您可以自由导航到应用程序的不同页面,正在运行的任务的状态始终都会展示出来
为了对span>
元素的百分比的动态更新做准备,我将在JavaScript端编写一个辅助函数:
app / templates / base.html:动态更新任务进度的辅助函数
...
{% block scripts %}
...
<script>
...
function set_task_progress(task_id, progress) {
$('#' + task_id + '-progress').text(progress);
}