Flask中的闪现和AJAX的url_for
我现在遇到了一个比较“棘手”的问题,我会尽量清晰简洁地说明一下情况。 我正在用Python开发一个工具,使用Flask框架。这个工具是为了在内部网络上使用。我的想法是有一个客户页面,里面有客户的名字、其他信息和一个很大的检查列表。
这些信息已经写在输入框里,用户只需要在那儿编辑,按下回车,页面就会重新加载并显示编辑后的信息(还会有通知,使用闪烁消息和growl)。
这个很大的检查列表让我不得不使用AJAX技术,因为我希望用户勾选框和输入内容时,能够“实时”更新,而不需要重新加载页面。
因此,我也将基本的信息输入改为AJAX,以避免页面重新加载。
我遇到了两个问题:
第一个问题是关于Flask的消息闪烁功能。
我有一个方法可以更新数据库中的客户名字,闪烁一个消息(成功或失败,取决于很多因素),然后再显示页面。为了避免页面重新加载,我用AJAX来处理表单提交。
问题是,我的Python方法会闪烁消息。而一旦我回到HTML页面(因为是AJAX,没有重新加载),Jinja2的
get_flashed_message
函数什么也得不到,因为它没有更新。因此,我无法获取这些闪烁的消息。我该如何获取这些消息?我唯一想到的解决办法是放弃使用闪烁功能,自己写一个消息系统,从方法中返回消息,然后在JavaScript中处理。这似乎非常傻,因为闪烁功能是Flask的一个特性,而AJAX让它“失效”。我觉得我可能漏掉了什么。
(顺便提一下,直到现在,我的闪烁消息管理是在我的基础布局中完成的,它调用一个模板,遍历所有消息并将它们显示为growl通知)
第二个问题是关于url_for的。
这是我用来编辑客户名字的表单:
<form id="changeClientName" method="POST" action="{{ url_for('clients.edit_client_name', name=client.name) }}" style="display:inline !important;">
<input type="text" name="name" class="form-control" value="{{ client.name }}" >
<input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;"/>
</form>
如你所见,action属性使用url_for
来调用正确的方法来编辑客户。我的方法是这样的:
@mod.route('/edit/<name>/', methods=['POST'])
def edit_client_name(name):
# code here
在编辑客户名字之前和之后的端点会是/edit/the_client_name
和/edit/the_new_client_name
,举个例子。显然,一旦调用了这个方法,我的AJAX需要更改这个action属性,以替换为新的URL(以防用户想在不更改页面/不重新加载页面的情况下重新编辑名字)。这就是我的第二个问题。
新的名字存储在JavaScript中,而action仍然是旧的端点。因此,我必须使用新的名字调用url_for
。但我找不到将JavaScript变量传递给Jinja2的方法。
我想要的是:
url_for('clients.edit_client_name', name=client.name)
变成这个:
url_for('clients.edit_client_name', name=my_javascript_variable_one_way_or_another)
我只需要调用一个JavaScript函数来修改表单的属性,并用这个新的url_for
来修改我的action属性。但我找不到实现这个的办法,也就是将JavaScript变量传递给Jinja2。
所以这就是我的两个问题。我该如何让flash()
与AJAX兼容?另外,我该如何将JavaScript属性传递给Jinja2?
我能想到的唯一解决方案是自己编写一个消息系统,这样就违背了闪烁功能的初衷,使其在AJAX中变得无用,这听起来很傻;或者在我的模板中硬编码URL,这完全违背了Flask使URL与方法独立、URL变化极其灵活的初衷。
谢谢。
4 个回答
这是个很久以前的问题,但我也遇到过同样的情况,并找到了一种比较简单的方法来通过ajax获取flask的闪现消息。基本上,我做的是创建了一个叫做get_flashes的路由,这个路由会返回闪现消息的模板,然后我通过jQuery的load函数来使用这个路由,比如说 $('#msg-div').load('{{ url_for('main.get_flashes') }}'
get_flashes路由的代码:
@main.route('get-flashes')
def get_flashes():
return render_template('_flashes.html')
_flashes.html(这个模板用来获取闪现消息,更多内容可以查看flask的文档)
{%- with messages = get_flashed_messages(with_categories=true) -%}
{% if messages %}
{% for category, message in messages %}
{% if category == 'error' %}
<div class="alert alert-danger alert-dismissable fade show msg" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% else %}
<div class="alert alert-{{ category }} alert-dismissable fade show msg" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endif %}
{% endfor %}
{% endif %}
{%- endwith %}
你可以写一个简单的JavaScript方法,来替代Flask中的'flash'方法,只需要理解一下flash_message.html是怎么格式化的。
function flash(message,category){
if (category == 'error'){
var icon='icon-exclamation-sign';
category='danger';
}
else if (category == 'success')
var icon='icon-ok-sign';
else
var icon='icon-info-sign';
$('<div class="alert alert-'+category+'"><i class="'+icon+'"></i> <a class="close" data-dismiss="alert">×</a>'+ message +'</div>').prependTo('#maincontent').hide().slideDown();
$.smoothScroll({
scrollElement: $('body'),
scrollTarget: '#mainContent'
});
}
url_for可能会稍微复杂一点,但也是可以做到的。
我也遇到过这个需求。为了更详细地说明Paul Wand的回答,这里是我实现的方法(注意,生成的标记是针对Bootstrap 3的):
在模板中放一个空的div来显示闪现消息:
<div id="flash"></div>
你的AJAX调用:
$.post("/foo/bar", JSON.stringify({'calls': 'whatever'}), function(returnedData) {
$('#flash').append(flashMessage(JSON.parse(returnedData)));
});
然后使用解析函数:
var flashMessage = function(data){
html = '';
for (i=0; i<data.length; i++) {
html += '<div class="alert alert-' + data[i]['type'] + '"><a href="#" class="close" data-dismiss="alert">×</a>' + data[i].message + '</div>';
}
return html;
};
在处理AJAX请求的路由中,简单地返回一个看起来像这样的JSON对象:
[{'type': 'success', 'message': '这里是一条消息'}]
其中'type'是Bootstrap 3的状态类型,比如成功、信息、警告、危险。这个字典会被放在一个列表里,所以如果需要的话可以传多个消息。
如果响应已经是JSON格式,那么就不需要使用JSON.parse。在这种情况下,代码就简单了:
$('#flash').append(flashMessage(returnedData));
另外,如果你不需要多个闪现消息,只需把'append'替换为'html'就可以了。
我来试着解释一下这个问题,不过我不太确定我完全理解你的意思 :D。下面的代码没有经过测试,更像是伪代码!
你的第一个问题是(如果我理解没错的话),你在通过ajax进行部分更新,并且想在部分更新后再获取更新结果。其实,你应该在每次ajax调用后立即返回更新结果,而不是等到之后再去获取。所以,如果你的更新代码看起来像这样:
data = $('#my-form').serialize()
$.post(update_url, data, function (results) {
// Here you check results.
if (results.success) {
$('#update-div').message('Update Success')
} else {
$('#update-div').message('Update Failed: ' + results.error_msg)
}
})
那么你的更新路由应该有类似这样的代码:
from flask import jsonify
@app.route('/partialupdate/<int:userid>', methods=['POST'])
def partial_update(userid):
try:
# fetch the user, perform the updates and commit
return jsonify(success=1)
except Exception, e:
return jsonify(success=0, error_msg=str(e))
至于你的第二个问题,关于生成URL的部分,可以通过使用用户ID来解决,而不是用客户名称。绝对不要用客户名称做其他用途,除了搜索 :D。在这种情况下,使用用户ID。用户ID通常是数据库中某个用户的主键。如果你没有使用数据库,那就自己生成用户ID,并把它和用户关联起来——每个用户应该有一个唯一的号码。使用用户ID的好处是,你可以始终用类似'/edituser/1'这样的URL进行编辑。